راه‌اندازهای مسدودسازی احراز هویت


توابع مسدودسازی به شما امکان می‌دهند کد سفارشی را اجرا کنید که نتیجه ثبت‌نام یا ورود کاربر به برنامه شما را تغییر می‌دهد. برای مثال، می‌توانید از اصالت‌سنجی کاربر درصورت عدم رعایت معیارهای خاص جلوگیری کنید، یا اطلاعات کاربر را قبل‌از بازگرداندن آن به برنامه مشتری خود به‌روز کنید.

قبل از شروع

برای استفاده از توابع مسدودسازی، باید پروژه Firebase خود را به Firebase Authentication with Identity Platform ارتقا دهید. اگر قبلاً ارتقا نداده‌اید، ابتدا این کار را انجام دهید.

آشنایی با توابع مسدودسازی

می‌توانید توابع مسدودسازی را برای این رویدادها ثبت کنید:

  • قبل‌از ایجاد کاربر: قبل‌از ذخیره شدن کاربر جدید در پایگاه داده Firebase Authentication و قبل‌از برگرداندن یک نمانه به برنامه کارخواه شما، راه‌اندازی می‌شود.

  • قبل‌از ورود کاربر به سیستم: پس‌از درستی‌سنجی اطلاعات اعتباری کاربر راه‌اندازی می‌شود، اما قبل‌از اینکه Firebase Authentication کد شناسه را به برنامه کارخواه شما برگرداند. اگر برنامه شما از اصالت‌سنجی چندعاملی استفاده می‌کند، این تابع پس‌از درستی‌سنجی عامل دوم توسط کاربر راه‌اندازی می‌شود. توجه داشته باشید که ایجاد کاربر جدید نیز هر دو این رویدادها را راه‌اندازی می‌کند.

  • قبل‌از ارسال ایمیل (فقط Node.js): قبل‌از ارسال ایمیل (برای مثال، ایمیل ورود به سیستم یا بازنشانی گذرواژه) به کاربر، راه‌اندازی می‌شود.

  • قبل‌از ارسال پیامک (فقط Node.js): قبل‌از ارسال پیامک به کاربر، برای مواردی مانند اصالت‌سنجی چندعاملی، راه‌اندازی می‌شود.

هنگام استفاده از توابع مسدودسازی، موارد زیر را درنظر داشته باشید:

  • تابع شما باید ظرف ۷ ثانیه پاسخ دهد. پس‌از ۷ ثانیه، Firebase Authentication خطا برمی‌گرداند و عملیات کارخواه ناموفق می‌شود.

  • کدهای پاسخ HTTP به‌غیراز 200 به برنامه‌های مشتری شما ارسال می‌شود. مطمئن شوید کد کارخواه شما هرگونه خطایی را که تابع شما می‌تواند برگرداند مدیریت می‌کند.

  • توابع برای همه کاربران پروژه شما اعمال می‌شود، ازجمله هر کاربری که در یک مستأجر قرار دارد. Firebase Authentication اطلاعاتی درباره کاربران به تابع شما ارائه می‌دهد، ازجمله هر مستأجری که به آن تعلق دارند، تا بتوانید به‌طور مقتضی پاسخ دهید.

  • پیوند دادن ارائه‌دهنده هویت دیگری به حساب، هرگونه تابع beforeUserSignedIn ثبت‌شده را دوباره راه‌اندازی می‌کند.

  • اصالت‌سنجی ناشناس و سفارشی باعث راه‌اندازی عملکردهای مسدودسازی نمی‌شود.

پیاده‌سازی تابع مسدودسازی

برای درج کد سفارشی‌تان در جریان‌های اصالت‌سنجی کاربر، توابع مسدودسازی را مستقر کنید. پس‌از استقرار عملکردهای مسدودسازی، کد سفارشی شما باید باموفقیت تکمیل شود تا اصالت‌سنجی و ایجاد کاربر با موفقیت انجام شود.

تابع مسدودسازی را به همان روشی که هر تابع دیگری را مستقر می‌کنید، مستقر می‌کنید. (برای جزئیات، به صفحه Cloud Functions شروع به کار مراجعه کنید). به‌طور خلاصه:

  1. تابعی بنویسید که رویداد هدف را مدیریت کند.

    برای مثال، برای شروع، می‌توانید تابع بدون عملیاتی مانند تابع زیر را به منبع خود اضافه کنید:

    Node.js

    import {
      beforeUserCreated,
    } from "firebase-functions/v2/identity";
    
    export const beforecreated = beforeUserCreated((event) => {
      // TODO
      return;
    });
    

    پایتون

    @identity_fn.before_user_created()
    def created_noop(
        event: identity_fn.AuthBlockingEvent,
    ) -> identity_fn.BeforeCreateResponse | None:
        return
    

    مثال بالا پیاده‌سازی منطق اصالت‌سنجی سفارشی را حذف کرده است. برای آشنایی با نحوه پیاده‌سازی توابع مسدودسازی، بخش‌های زیر را ببینید و برای نمونه‌های خاص، به سناریوهای رایج مراجعه کنید.

  2. توابع خود را بااستفاده از Firebase CLI مستقر کنید:

    firebase deploy --only functions
    

    هر بار که توابع خود را به‌روز می‌کنید، باید آن‌ها را دوباره مستقر کنید.

درحال دریافت اطلاعات کاربر و بافت

رویدادهای مسدودکننده یک شیء AuthBlockingEvent ارائه می‌دهند که حاوی اطلاعاتی درباره کاربر واردشده به سیستم است. از این مقادیر در کدتان استفاده کنید تا مشخص شود آیا عملیات می‌تواند ادامه یابد یا نه.

این شیء حاوی ویژگی‌های زیر است:

نام شرح مثال
locale منطقه زبانی برنامه. می‌توانید بااستفاده از کیت توسعه نرم‌افزار کارخواه یا با ارسال سرصفحه منطقه زبانی در REST API، منطقه زبانی را تنظیم کنید. fr یا sv-SE
ipAddress نشانی IP دستگاهی که کاربر نهایی از آن ثبت‌نام یا ورود به سیستم می‌کند. 114.14.200.1
userAgent نماینده کاربر که عملکرد مسدودسازی را راه‌اندازی می‌کند. 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');

پایتون

raise https_fn.HttpsError(
    code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT)

همچنین می‌توانید پیام خطای سفارشی مشخص کنید:

Node.js

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

پایتون

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");
  }
});

پایتون

# 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");

پایتون

# 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",
    )

برنامه‌تان باید خطا را شناسایی کند و براین‌اساس آن را مدیریت کند. برای مثال:

جاوا اسکریپت

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 ذخیره شده است و به کارخواه برگردانده می‌شود اصلاح کنید.

برای اصلاح کاربر، شیئی را از گرداننده رویداد خود برگردانید که حاوی فیلدهای موردنظر برای اصلاح باشد. می‌توانید فیلدهای زیر را اصلاح کنید:

  • displayName
  • disabled
  • emailVerified
  • photoURL
  • customClaims
  • ‫sessionClaims (فقط beforeUserSignedIn)

به‌استثنای sessionClaims، همه فیلدهای اصلاح‌شده در پایگاه داده Firebase Authentication ذخیره می‌شوند، یعنی در کد پاسخ گنجانده می‌شوند و بین جلسه‌های کاربر باقی می‌مانند.

مثال زیر نشان می‌دهد که چگونه نام نمایشی پیش‌فرض را تنظیم کنید:

Node.js

export const beforecreated = beforeUserCreated((event) => {
  return {
    // If no display name is provided, set it to "Guest".
    displayName: event.data.displayName || 'Guest'
  };
});

پایتون

@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 را در هر دو کنترل‌کننده رویداد تنظیم کنید، مقدار تنظیم‌شده در beforeUserSignedIn روی مقدار تنظیم‌شده در beforeUserCreated بازنویسی می‌شود. فقط برای sessionClaims، آن‌ها به ادعاهای توکن جلسه فعلی منتشر می‌شوند، اما در پایگاه داده ذخیره یا نگهداری نمی‌شوند.

برای مثال، اگر هرگونه sessionClaims تنظیم شده باشد، beforeUserSignedIn آن‌ها را با هرگونه ادعای beforeUserCreated برمی‌گرداند و ادغام می‌شوند. وقتی ادغام می‌شوند، اگر کلید sessionClaims با کلیدی در customClaims مطابقت داشته باشد، customClaims منطبق در ادعاهای کد با کلید sessionClaims رونویسی خواهد شد. بااین‌حال، کلید customClaims رونویسی‌شده همچنان در پایگاه داده برای درخواست‌های آتی باقی خواهد ماند.

اعتبارنامه‌ها و داده‌های OAuth پشتیبانی‌شده

می‌توانید اطلاعات کاربری و داده‌های OAuth را از ارائه‌دهندگان هویت مختلف به توابع مسدودکننده ارسال کنید. جدول زیر نشان می‌دهد چه اطلاعات کاربری و داده‌هایی برای هر ارائه‌دهنده هویت پشتیبانی می‌شود:

ارائه‌دهنده هویت کد شناسه کد دسترسی زمان انقضا رمز کد ژتون بازآوری ادعاهای ورود به سیستم
Google بله بله بله نه بله نه
فیس‌بوک نه بله بله نه نه نه
Twitter نه بله نه بله نه نه
GitHub نه بله نه نه نه نه
Microsoft بله بله بله نه بله نه
LinkedIn نه بله بله نه نه نه
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

وقتی کاربری با Google به سیستم وارد می‌شود، اطلاعات کاربری زیر منتقل خواهد شد:

  • شناسه داده‌واحد
  • کد دسترسی
  • ژتون بازآوری: فقط درصورتی ارائه می‌شود که پارامترهای سفارشی زیر درخواست شده باشد:
    • access_type=offline
    • prompt=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 scope انتخاب شده باشد، به تابع مسدودسازی ارسال می‌شود.

مثال:

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

وقتی کاربر با 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');
  }
});

پایتون

 @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');
  }
});

پایتون

@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,
    };
  }
});

پایتون

@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!');
  }
});

پایتون

@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,
            },
        };
    }
});

پایتون

@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ها از مناطق جغرافیایی مختلفی هستند)، می‌توانید از کاربر بخواهید دوباره به سیستم وارد شود.

  1. از ادعاهای جلسه برای ردیابی نشانی IP که کاربر با آن به سیستم وارد می‌شود استفاده کنید:

    Node.js

    export const beforesignedin = beforeUserSignedIn((event) => {
      return {
        sessionClaims: {
          signInIpAddress: event.ipAddress,
        },
      };
    });
    

    پایتون

    @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})
    
  2. وقتی کاربری تلاش می‌کند به منابعی دسترسی پیدا کند که نیاز به اصالت‌سنجی با 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!' })
          });
        }
      });
    });
    

    پایتون

    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,
          };
        }
      });
});

پایتون

@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 Calendar APIs استفاده کنید. ژتون بازآوری برای دسترسی آفلاین ذخیره می‌شود.

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();
          });
      });
    })
  }
});

پایتون

@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',
 }
});