ฟังก์ชันการบล็อกช่วยให้คุณเรียกใช้โค้ดที่กำหนดเองซึ่งจะแก้ไขผลลัพธ์ของการลงทะเบียนหรือลงชื่อเข้าใช้แอปของผู้ใช้ได้ เช่น คุณสามารถป้องกันไม่ให้ผู้ใช้ตรวจสอบสิทธิ์หากไม่เป็นไปตามเกณฑ์ที่กำหนด หรืออัปเดตข้อมูลของผู้ใช้ก่อนที่จะส่งกลับไปยังแอปไคลเอ็นต์
ก่อนเริ่มต้น
หากต้องการใช้ฟังก์ชันการบล็อก คุณต้องอัปเกรดโปรเจ็กต์ Firebase เป็น Firebase Authentication with Identity Platform หากยังไม่ได้อัปเกรด โปรดอัปเกรดก่อน
ทำความเข้าใจฟังก์ชันการบล็อก
คุณสามารถลงทะเบียนฟังก์ชันการบล็อกสำหรับเหตุการณ์ต่อไปนี้ได้
ก่อนสร้างผู้ใช้: ทริกเกอร์ก่อนบันทึกผู้ใช้ใหม่ลงในฐานข้อมูล Firebase Authentication และก่อนแสดงผลโทเค็นไปยังแอปไคลเอ็นต์
ก่อนที่ผู้ใช้จะลงชื่อเข้าใช้: ทริกเกอร์หลังจากยืนยันข้อมูลเข้าสู่ระบบของผู้ใช้แล้ว แต่ก่อนที่ Firebase Authentication จะแสดงผลโทเค็นระบุตัวตนไปยังแอปไคลเอ็นต์ หากแอปใช้การตรวจสอบสิทธิ์แบบหลายปัจจัย ฟังก์ชันจะทริกเกอร์หลังจากที่ผู้ใช้ยืนยันปัจจัยที่ 2 โปรดทราบว่าการสร้างผู้ใช้ใหม่จะทริกเกอร์ทั้ง 2 เหตุการณ์นี้ด้วย
ก่อนส่งอีเมล (Node.js เท่านั้น): ทริกเกอร์ก่อนส่งอีเมล (เช่น อีเมลลงชื่อเข้าใช้หรือรีเซ็ตรหัสผ่าน) ไปยังผู้ใช้
ก่อนส่งข้อความ SMS (Node.js เท่านั้น): ทริกเกอร์ก่อนที่จะส่งข้อความ SMS ไปยังผู้ใช้ สำหรับกรณีต่างๆ เช่น การตรวจสอบสิทธิ์แบบหลายปัจจัย
โปรดคำนึงถึงสิ่งต่อไปนี้เมื่อใช้ฟังก์ชันการบล็อก
ฟังก์ชันต้องตอบกลับภายใน 7 วินาที หลังจากผ่านไป 7 วินาที Firebase Authentication จะแสดงข้อผิดพลาดและการดำเนินการของไคลเอ็นต์จะล้มเหลว
ระบบจะส่งรหัสการตอบกลับ HTTP อื่นๆ นอกเหนือจาก
200ไปยังแอปไคลเอ็นต์ ตรวจสอบว่าโค้ดไคลเอ็นต์จัดการข้อผิดพลาดที่ฟังก์ชันอาจแสดงผลฟังก์ชันจะมีผลกับผู้ใช้ทุกคนในโปรเจ็กต์ รวมถึงผู้ใช้ที่อยู่ในกลุ่มผู้ใช้ Firebase Authentication จะให้ข้อมูลเกี่ยวกับผู้ใช้แก่ฟังก์ชันของคุณ รวมถึงผู้เช่าที่ผู้ใช้เป็นสมาชิก เพื่อให้คุณตอบสนองได้อย่างเหมาะสม
การลิงก์ผู้ให้บริการข้อมูลประจำตัวรายอื่นกับบัญชีจะทริกเกอร์ฟังก์ชัน
beforeUserSignedInที่ลงทะเบียนไว้อีกครั้งการตรวจสอบสิทธิ์แบบไม่ระบุตัวตนและการตรวจสอบสิทธิ์ที่กำหนดเองจะไม่เรียกใช้ฟังก์ชันการบล็อก
ติดตั้งใช้งานฟังก์ชันการบล็อก
หากต้องการแทรกโค้ดที่กำหนดเองลงในขั้นตอนการตรวจสอบสิทธิ์ผู้ใช้ ให้ติดตั้งใช้งานฟังก์ชันการบล็อก เมื่อติดตั้งใช้งานฟังก์ชันการบล็อกแล้ว โค้ดที่กำหนดเองของคุณต้องทำงานเสร็จสมบูรณ์เพื่อให้การตรวจสอบสิทธิ์และการสร้างผู้ใช้สำเร็จ
คุณทำให้ฟังก์ชันการบล็อกใช้งานได้ในลักษณะเดียวกับที่ทำให้ฟังก์ชันใดๆ ใช้งานได้ (ดูรายละเอียดได้ที่หน้าCloud Functions เริ่มต้นใช้งาน) บทสรุปมีดังนี้:
เขียนฟังก์ชันที่จัดการเหตุการณ์เป้าหมาย
ตัวอย่างเช่น หากต้องการเริ่มต้นใช้งาน คุณสามารถเพิ่มฟังก์ชัน No-op เช่นโค้ดต่อไปนี้ลงในแหล่งที่มา
Node.js
import { beforeUserCreated, } from "firebase-functions/v2/identity"; export const beforecreated = beforeUserCreated((event) => { // TODO return; });Python
@identity_fn.before_user_created() def created_noop( event: identity_fn.AuthBlockingEvent, ) -> identity_fn.BeforeCreateResponse | None: returnตัวอย่างข้างต้นได้ละเว้นการติดตั้งใช้งานตรรกะการตรวจสอบสิทธิ์ที่กำหนดเอง ดูส่วนต่อไปนี้เพื่อดูวิธีใช้ฟังก์ชันการบล็อกและสถานการณ์ที่พบบ่อยสำหรับตัวอย่างที่เฉพาะเจาะจง
ทำให้ฟังก์ชันใช้งานได้โดยใช้ Firebase CLI:
firebase deploy --only functionsคุณต้องทำให้ฟังก์ชันใช้งานได้อีกครั้งทุกครั้งที่อัปเดต
การรับข้อมูลผู้ใช้และบริบท
เหตุการณ์การบล็อกจะแสดงAuthBlockingEvent
ออบเจ็กต์ที่มีข้อมูลเกี่ยวกับผู้ใช้ที่ลงชื่อเข้าใช้ ใช้ค่าเหล่านี้ในโค้ดเพื่อพิจารณาว่าจะอนุญาตให้ดำเนินการต่อหรือไม่
ออบเจ็กต์มีพร็อพเพอร์ตี้ต่อไปนี้
| ชื่อ | คำอธิบาย | ตัวอย่าง |
|---|---|---|
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 Authentication |
projects/project-id/tenants/tenant-id
|
timestamp
| เวลาที่ทริกเกอร์เหตุการณ์ ซึ่งอยู่ในรูปแบบสตริง RFC 3339 | Tue, 23 Jul 2019 21:10:57 GMT
|
additionalUserInfo
| ออบเจ็กต์ที่มีข้อมูลเกี่ยวกับผู้ใช้ |
AdditionalUserInfo
|
credential
| ออบเจ็กต์ที่มีข้อมูลเกี่ยวกับข้อมูลเข้าสู่ระบบของผู้ใช้ |
AuthCredential
|
การบล็อกการลงทะเบียนหรือการลงชื่อเข้าใช้
หากต้องการบล็อกการพยายามลงทะเบียนหรือลงชื่อเข้าใช้ ให้ส่ง HttpsError ในฟังก์ชัน เช่น
Node.js
import { HttpsError } from "firebase-functions/v2/identity";
throw new HttpsError('invalid-argument');
Python
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT)
นอกจากนี้ คุณยังระบุข้อความแสดงข้อผิดพลาดที่กำหนดเองได้ด้วย โดยทำดังนี้
Node.js
throw new HttpsError('permission-denied', 'Unauthorized request origin!');
Python
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.PERMISSION_DENIED,
message="Unauthorized request origin!"
)
ตัวอย่างต่อไปนี้แสดงวิธีบล็อกผู้ใช้ที่ไม่ได้อยู่ในโดเมนที่เฉพาะเจาะจงไม่ให้ลงทะเบียนแอปของคุณ
Node.js
export const beforecreated = beforeUserCreated((event) => {
const user = event.data;
// (If the user is authenticating within a tenant context, the tenant ID can be determined from
// user.tenantId or from event.resource, e.g. 'projects/project-id/tenant/tenant-id-1')
// Only users of a specific domain can sign up.
if (!user?.email?.includes('@acme.com')) {
throw new HttpsError('invalid-argument', "Unauthorized email");
}
});
Python
# Block account creation with any non-acme email address.
@identity_fn.before_user_created()
def validatenewuser(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
# User data passed in from the CloudEvent.
user = event.data
# Only users of a specific domain can sign up.
if user.email is None or "@acme.com" not in user.email:
# Return None so that Firebase Auth rejects the account creation.
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
message="Unauthorized email",
)
ไม่ว่าคุณจะใช้ข้อความเริ่มต้นหรือข้อความที่กำหนดเอง Cloud Functions จะรวมข้อผิดพลาดและแสดงผลไปยังไคลเอ็นต์เป็นข้อผิดพลาดภายใน เช่น
Node.js
throw new HttpsError('invalid-argument', "Unauthorized email");
Python
# Only users of a specific domain can sign up.
if user.email is None or "@acme.com" not in user.email:
# Return None so that Firebase Auth rejects the account creation.
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
message="Unauthorized email",
)
แอปของคุณควรตรวจจับข้อผิดพลาดและจัดการตามความเหมาะสม เช่น
JavaScript
import { getAuth, createUserWithEmailAndPassword } from 'firebase/auth';
// Blocking functions can also be triggered in a multi-tenant context before user creation.
// firebase.auth().tenantId = 'tenant-id-1';
const auth = getAuth();
try {
const result = await createUserWithEmailAndPassword(auth)
const idTokenResult = await result.user.getIdTokenResult();
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 Authentication และส่งกลับไปยังไคลเอ็นต์
หากต้องการแก้ไขผู้ใช้ ให้แสดงผลออบเจ็กต์จากตัวแฮนเดิลเหตุการณ์ที่มีช่องที่จะแก้ไข คุณแก้ไขช่องต่อไปนี้ได้
displayNamedisabledemailVerifiedphotoURLcustomClaimssessionClaims(beforeUserSignedInเท่านั้น)
ระบบจะบันทึกฟิลด์ที่แก้ไขทั้งหมดลงในฐานข้อมูลของ Firebase Authentication ยกเว้น sessionClaims ซึ่งหมายความว่าฟิลด์เหล่านี้จะรวมอยู่ในโทเค็นการตอบกลับและคงอยู่ระหว่างเซสชันของผู้ใช้
ตัวอย่างต่อไปนี้แสดงวิธีตั้งชื่อที่แสดงเริ่มต้น
Node.js
export const beforecreated = beforeUserCreated((event) => {
return {
// If no display name is provided, set it to "Guest".
displayName: event.data.displayName || 'Guest'
};
});
Python
@identity_fn.before_user_created()
def setdefaultname(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
return identity_fn.BeforeCreateResponse(
# If no display name is provided, set it to "Guest".
display_name=event.data.display_name if event.data.display_name is not None else "Guest"
)
หากคุณลงทะเบียนเครื่องจัดการเหตุการณ์สำหรับทั้ง beforeUserCreated และ beforeUserSignedIn โปรดทราบว่า beforeUserSignedIn จะทำงานหลังจาก beforeUserCreated ช่องผู้ใช้ที่อัปเดตใน beforeUserCreated จะปรากฏใน beforeUserSignedIn หากคุณตั้งค่าช่องอื่นนอกเหนือจาก sessionClaims ในเครื่องจัดการเหตุการณ์ทั้ง 2 รายการ ค่าที่ตั้งไว้ใน beforeUserSignedIn จะเขียนทับค่าที่ตั้งไว้ใน beforeUserCreated สำหรับ sessionClaims เท่านั้น ระบบจะเผยแพร่แอตทริบิวต์ไปยังการอ้างสิทธิ์โทเค็นของเซสชันปัจจุบัน แต่จะไม่เก็บหรือจัดเก็บไว้ในฐานข้อมูล
ตัวอย่างเช่น หากมีการตั้งค่า sessionClaims ไว้ beforeUserSignedIn จะแสดงผลลัพธ์พร้อมกับการอ้างสิทธิ์ beforeUserCreated และจะรวมเข้าด้วยกัน เมื่อผสานรวมกันแล้ว หากคีย์ sessionClaims ตรงกับคีย์ใน customClaims ระบบจะเขียนทับ customClaims ที่ตรงกันในการอ้างสิทธิ์โทเค็นด้วยคีย์ sessionClaims อย่างไรก็ตาม คีย์ customClaims ที่ถูกเขียนทับจะยังคงอยู่ในฐานข้อมูลสำหรับคำขอในอนาคต
ข้อมูลเข้าสู่ระบบ OAuth และข้อมูลที่รองรับ
คุณสามารถส่งข้อมูลเข้าสู่ระบบ OAuth และข้อมูลไปยังฟังก์ชันการบล็อกจากผู้ให้บริการข้อมูลประจำตัวต่างๆ ได้ ตารางต่อไปนี้แสดงข้อมูลเข้าสู่ระบบและข้อมูลที่รองรับสำหรับผู้ให้บริการข้อมูลประจำตัวแต่ละราย
| ผู้ให้บริการข้อมูลประจำตัว | โทเค็นระบุตัวตน | โทเค็นเพื่อการเข้าถึง | เวลาหมดอายุ | ข้อมูลลับของโทเค็น | โทเค็นการรีเฟรช | การอ้างสิทธิ์การลงชื่อเข้าใช้ |
|---|---|---|---|---|---|---|
| ใช่ | ใช่ | ใช่ | ไม่ใช่ | ใช่ | ไม่ | |
| ไม่ | ใช่ | ใช่ | ไม่ได้ | ไม่ใช่ | ไม่ | |
| ไม่ | ใช่ | ไม่ใช่ | ใช่ | ไม่ได้ | ไม่ | |
| GitHub | ไม่ | ใช่ | ไม่ได้ | ไม่ใช่ | ไม่ใช่ | ไม่ |
| Microsoft | ใช่ | ใช่ | ใช่ | ไม่ใช่ | ใช่ | ไม่ |
| ไม่ | ใช่ | ใช่ | ไม่ได้ | ไม่ใช่ | ไม่ | |
| Yahoo | ใช่ | ใช่ | ใช่ | ไม่ใช่ | ใช่ | ไม่ |
| Apple | ใช่ | ใช่ | ใช่ | ไม่ใช่ | ใช่ | ไม่ |
| SAML | ไม่ | ไม่ใช่ | ไม่ใช่ | ไม่ใช่ | ไม่ได้ | ใช่ |
| OIDC | ใช่ | ใช่ | ใช่ | ไม่ใช่ | ใช่ | ใช่ |
โทเค็น OAuth
หากต้องการใช้โทเค็นระบุตัวตน โทเค็นเพื่อการเข้าถึง หรือโทเค็นการรีเฟรชในฟังก์ชันการบล็อก คุณต้องเลือกช่องทำเครื่องหมายในหน้าฟังก์ชันการบล็อกของคอนโซล 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 ระบบจะส่งข้อมูลเข้าสู่ระบบต่อไปนี้
- โทเค็นระบุตัวตน
- โทเค็นเพื่อการเข้าถึง
- โทเค็นการรีเฟรช: จะระบุไว้ก็ต่อเมื่อมีการขอพารามิเตอร์ที่กำหนดเองต่อไปนี้
access_type=offlineprompt=consentหากผู้ใช้เคยให้ความยินยอมไว้ก่อนหน้านี้และไม่มีการขอขอบเขตใหม่
ตัวอย่าง
import { getAuth, signInWithPopup, GoogleAuthProvider } from 'firebase/auth';
const auth = getAuth();
const provider = new GoogleAuthProvider();
provider.setCustomParameters({
'access_type': 'offline',
'prompt': 'consent'
});
signInWithPopup(auth, provider);
ดูข้อมูลเพิ่มเติมเกี่ยวกับโทเค็นการรีเฟรชของ Google
เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วย Facebook ระบบจะส่งข้อมูลเข้าสู่ระบบต่อไปนี้
- โทเค็นเพื่อการเข้าถึง: ระบบจะแสดงผลโทเค็นเพื่อการเข้าถึงที่สามารถแลกเปลี่ยนเป็นโทเค็นเพื่อการเข้าถึงอื่นได้ ดูข้อมูลเพิ่มเติมเกี่ยวกับโทเค็นเพื่อการเข้าถึงประเภทต่างๆ ที่ Facebook รองรับ และวิธีแลกเปลี่ยนเป็นโทเค็นที่มีอายุการใช้งานยาวนาน
GitHub
เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วย GitHub ระบบจะส่งข้อมูลเข้าสู่ระบบต่อไปนี้
- โทเค็นเพื่อการเข้าถึง: ไม่หมดอายุเว้นแต่จะถูกเพิกถอน
Microsoft
เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วย Microsoft ระบบจะส่งข้อมูลเข้าสู่ระบบต่อไปนี้
- โทเค็นระบุตัวตน
- โทเค็นเพื่อการเข้าถึง
- โทเค็นการรีเฟรช: ส่งไปยังฟังก์ชันการบล็อกหากเลือกขอบเขต
offline_accessไว้
ตัวอย่าง
import { getAuth, signInWithPopup, OAuthProvider } from 'firebase/auth';
const auth = getAuth();
const provider = new OAuthProvider('microsoft.com');
provider.addScope('offline_access');
signInWithPopup(auth, provider);
Yahoo
เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วย Yahoo ระบบจะส่งข้อมูลเข้าสู่ระบบต่อไปนี้โดยไม่มีพารามิเตอร์ที่กำหนดเองหรือขอบเขต
- โทเค็นระบุตัวตน
- โทเค็นเพื่อการเข้าถึง
- โทเค็นการรีเฟรช
เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วย LinkedIn ระบบจะส่งข้อมูลเข้าสู่ระบบต่อไปนี้
- โทเค็นเพื่อการเข้าถึง
Apple
เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วย Apple ระบบจะส่งข้อมูลเข้าสู่ระบบต่อไปนี้โดยไม่มีพารามิเตอร์ที่กำหนดเองหรือขอบเขต
- โทเค็นระบุตัวตน
- โทเค็นเพื่อการเข้าถึง
- โทเค็นการรีเฟรช
สถานการณ์ที่พบบ่อย
ตัวอย่างต่อไปนี้แสดงกรณีการใช้งานที่พบบ่อยสำหรับฟังก์ชันการบล็อก
อนุญาตให้ลงทะเบียนจากโดเมนที่เฉพาะเจาะจงเท่านั้น
ตัวอย่างต่อไปนี้แสดงวิธีป้องกันไม่ให้ผู้ใช้ที่ไม่ได้อยู่ในโดเมน example.com ลงทะเบียนกับแอปของคุณ
Node.js
export const beforecreated = beforeUserCreated((event) => {
const user = event.data;
if (!user?.email?.includes('@example.com')) {
throw new HttpsError(
'invalid-argument', 'Unauthorized email');
}
});
Python
@identity_fn.before_user_created()
def validatenewuser(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
# User data passed in from the CloudEvent.
user = event.data
# Only users of a specific domain can sign up.
if user.email is None or "@example.com" not in user.email:
# Return None so that Firebase Auth rejects the account creation.
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
message="Unauthorized email",
)
การบล็อกผู้ใช้ที่มีอีเมลที่ยังไม่ได้รับการยืนยันไม่ให้ลงทะเบียน
ตัวอย่างต่อไปนี้แสดงวิธีป้องกันไม่ให้ผู้ใช้ที่มีอีเมลที่ยังไม่ได้รับการยืนยันลงทะเบียนกับแอปของคุณ
Node.js
export const beforecreated = beforeUserCreated((event) => {
const user = event.data;
if (user.email && !user.emailVerified) {
throw new HttpsError(
'invalid-argument', 'Unverified email');
}
});
Python
@identity_fn.before_user_created()
def requireverified(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
if event.data.email is not None and not event.data.email_verified:
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
message="You must register using a trusted provider.",
)
การถือว่าอีเมลของผู้ให้บริการข้อมูลประจำตัวบางรายได้รับการยืนยันแล้ว
ตัวอย่างต่อไปนี้แสดงวิธีจัดการอีเมลของผู้ใช้จากผู้ให้บริการข้อมูลประจำตัวบางรายเป็นอีเมลที่ได้รับการยืนยัน
Node.js
export const beforecreated = beforeUserCreated((event) => {
const user = event.data;
if (user.email && !user.emailVerified && event.eventType.includes(':facebook.com')) {
return {
emailVerified: true,
};
}
});
Python
@identity_fn.before_user_created()
def markverified(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
if event.data.email is not None and "@facebook.com" in event.data.email:
return identity_fn.BeforeSignInResponse(email_verified=True)
การบล็อกการลงชื่อเข้าใช้จากที่อยู่ IP บางรายการ
ตัวอย่างต่อไปนี้แสดงวิธีบล็อกการลงชื่อเข้าใช้จากช่วงที่อยู่ IP บางช่วง
Node.js
export const beforesignedin = beforeUserSignedIn((event) => {
if (isSuspiciousIpAddress(event.ipAddress)) {
throw new HttpsError(
'permission-denied', 'Unauthorized access!');
}
});
Python
@identity_fn.before_user_signed_in()
def ipban(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeSignInResponse | None:
if is_suspicious(event.ip_address):
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.PERMISSION_DENIED, message="IP banned."
)
การตั้งค่าการอ้างสิทธิ์ที่กำหนดเองและเซสชัน
ตัวอย่างต่อไปนี้แสดงวิธีตั้งค่าการอ้างสิทธิ์ที่กำหนดเองและการอ้างสิทธิ์เซสชัน
Node.js
export const beforecreated = beforeUserCreated((event) => {
if (event.credential &&
event.credential.claims &&
event.credential.providerId === "saml.my-provider-id") {
return {
// Employee ID does not change so save in persistent claims (stored in
// Auth DB).
customClaims: {
eid: event.credential.claims.employeeid,
},
};
}
});
export const beforesignin = beforeUserSignedIn((event) => {
if (event.credential &&
event.credential.claims &&
event.credential.providerId === "saml.my-provider-id") {
return {
// Copy role and groups to token claims. These will not be persisted.
sessionClaims: {
role: event.credential.claims.role,
groups: event.credential.claims.groups,
},
};
}
});
Python
@identity_fn.before_user_created()
def setemployeeid(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
if (
event.credential is not None
and event.credential.claims is not None
and event.credential.provider_id == "saml.my-provider-id"
):
return identity_fn.BeforeCreateResponse(
custom_claims={"eid": event.credential.claims["employeeid"]}
)
@identity_fn.before_user_signed_in()
def copyclaimstosession(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeSignInResponse | None:
if (
event.credential is not None
and event.credential.claims is not None
and event.credential.provider_id == "saml.my-provider-id"
):
return identity_fn.BeforeSignInResponse(
session_claims={
"role": event.credential.claims["role"],
"groups": event.credential.claims["groups"],
}
)
การติดตามที่อยู่ IP เพื่อตรวจสอบกิจกรรมที่น่าสงสัย
คุณสามารถป้องกันการขโมยโทเค็นได้โดยการติดตามที่อยู่ IP ที่ผู้ใช้ลงชื่อเข้าใช้ และเปรียบเทียบกับที่อยู่ IP ในคำขอต่อๆ มา หากคำขอดูมีพิรุธ เช่น IP มาจากภูมิภาคต่างๆ คุณสามารถขอให้ผู้ใช้ลงชื่อเข้าใช้อีกครั้งได้
ใช้การอ้างสิทธิ์เซสชันเพื่อติดตามที่อยู่ IP ที่ผู้ใช้ใช้ลงชื่อเข้าใช้ โดยทำดังนี้
Node.js
export const beforesignedin = beforeUserSignedIn((event) => { return { sessionClaims: { signInIpAddress: event.ipAddress, }, }; });Python
@identity_fn.before_user_signed_in() def logip( event: identity_fn.AuthBlockingEvent, ) -> identity_fn.BeforeSignInResponse | None: return identity_fn.BeforeSignInResponse(session_claims={"signInIpAddress": event.ip_address})เมื่อผู้ใช้พยายามเข้าถึงทรัพยากรที่ต้องมีการตรวจสอบสิทธิ์ด้วย Firebase Authentication ให้เปรียบเทียบที่อยู่ 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!' }) }); } }); });Python
from firebase_admin import auth, initialize_app import flask initialize_app() flask_app = flask.Flask(__name__) @flask_app.post() def get_restricted_data(req: flask.Request): # Get the ID token passed. id_token = req.json().get("idToken") # Verify the ID token, check if revoked, and decode its payload. try: claims = auth.verify_id_token(id_token, check_revoked=True) except: return flask.Response(status=500) # Get request IP address. request_ip = req.remote_addr # Get sign-in IP address. signin_ip = 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 is_suspicious_change(signin_ip, request_ip): # Suspicious IP address change. Require re-authentication. # You can also revoke all user sessions by calling: # auth.revoke_refresh_tokens(claims["sub"]) return flask.Response(status=401, response="Unauthorized access. Sign in again!") else: # Access is valid. Try to return data. return data_from_claims(claims)
การคัดกรองรูปภาพของผู้ใช้
ตัวอย่างต่อไปนี้แสดงวิธีล้างข้อมูลรูปโปรไฟล์ของผู้ใช้
Node.js
export const beforecreated = beforeUserCreated((event) => {
const user = event.data;
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,
};
}
});
});
Python
@identity_fn.before_user_created()
def sanitizeprofilephoto(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
if event.data.photo_url is not None:
score = analyze_photo_with_ml(event.data.photo_url)
if score > THRESHOLD:
return identity_fn.BeforeCreateResponse(photo_url=PLACEHOLDER_URL)
ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีตรวจจับและล้างรูปภาพได้ในเอกสารประกอบของ Cloud Vision
การเข้าถึงข้อมูลเข้าสู่ระบบ OAuth ของผู้ให้บริการข้อมูลประจำตัวของผู้ใช้
ตัวอย่างต่อไปนี้แสดงวิธีรับโทเค็นการรีเฟรชสำหรับผู้ใช้ที่ลงชื่อเข้าใช้ด้วย Google และใช้โทเค็นดังกล่าวเพื่อเรียก Google ปฏิทิน API ระบบจะจัดเก็บโทเค็นการรีเฟรชไว้สำหรับการเข้าถึงแบบออฟไลน์
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
);
export const beforecreated = beforeUserCreated((event) => {
const user = event.data;
if (event.credential &&
event.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,
event.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:
// event.additionalUserInfo.profile.granted_scopes (space joined list of scopes).
// Set access token/refresh token.
oAuth2Client.setCredentials({
access_token: event.credential.accessToken,
refresh_token: event.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();
});
});
})
}
});
Python
@identity_fn.before_user_created()
def savegoogletoken(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
"""During sign-up, save the Google OAuth2 access token and queue up a task
to schedule an onboarding session on the user's Google Calendar.
You will only get an access token if you enabled it in your project's blocking
functions settings in the Firebase console:
https://console.firebase.google.com/project/_/authentication/settings
"""
if event.credential is not None and event.credential.provider_id == "google.com":
print(f"Signed in with {event.credential.provider_id}. Saving access token.")
firestore_client: google.cloud.firestore.Client = firestore.client()
doc_ref = firestore_client.collection("user_info").document(event.data.uid)
doc_ref.set({"calendar_access_token": event.credential.access_token}, merge=True)
tasks_client = google.cloud.tasks_v2.CloudTasksClient()
task_queue = tasks_client.queue_path(
params.PROJECT_ID.value, options.SupportedRegion.US_CENTRAL1.value, "scheduleonboarding"
)
target_uri = get_function_url("scheduleonboarding")
calendar_task = google.cloud.tasks_v2.Task(
http_request={
"http_method": google.cloud.tasks_v2.HttpMethod.POST,
"url": target_uri,
"headers": {"Content-type": "application/json"},
"body": json.dumps({"data": {"uid": event.data.uid}}).encode(),
},
schedule_time=datetime.now() + timedelta(minutes=1),
)
tasks_client.create_task(parent=task_queue, task=calendar_task)
การลบล้างคำตัดสินของ reCAPTCHA Enterprise สำหรับการดำเนินการของผู้ใช้
ตัวอย่างต่อไปนี้แสดงวิธีลบล้างคำตัดสินของ reCAPTCHA Enterprise สำหรับโฟลว์ผู้ใช้ที่รองรับ
ดูข้อมูลเพิ่มเติมเกี่ยวกับการผสานรวม reCAPTCHA Enterprise กับการตรวจสอบสิทธิ์ Firebase ได้ที่เปิดใช้ reCAPTCHA Enterprise
คุณสามารถใช้ฟังก์ชันการบล็อกเพื่ออนุญาตหรือบล็อกขั้นตอนตามปัจจัยที่กำหนดเอง ซึ่งจะลบล้างผลลัพธ์ที่ reCAPTCHA Enterprise ระบุ
Node.js
const { beforeSmsSent } = require("firebase-functions/v2/identity");
exports.beforesmssentv2 = beforeSmsSent((event) => {
if (
event.smsType === "SIGN_IN_OR_SIGN_UP" &&
event.additionalUserInfo.phoneNumber.includes('+91')
) {
return {
recaptchaActionOverride: "ALLOW",
};
}
// Allow users to sign in with recaptcha score greater than 0.5
if (event.additionalUserInfo.recaptchaScore > 0.5) {
return {
recaptchaActionOverride: 'ALLOW',
};
}
// Block all others.
return {
recaptchaActionOverride: 'BLOCK',
}
});