Extend Firebase Authentication with blocking functions Stay organized with collections Save and categorize content based on your preferences.
Blocking functions let you execute custom code that modifies the result of auser registering or signing in to your app. For example, you can prevent a userfrom authenticating if they don't meet certain criteria, or update a user'sinformation before returning it to your client app.
Before you begin
To use blocking functions you must upgrade your Firebase project toFirebase Authentication with Identity Platform.If you haven't already upgraded, do so first.
Understanding blocking functions
You can register blocking functions for these events:
Before the user is created: Triggers before a new user is saved to theFirebase Authentication database, and before a token is returned to yourclient app.
Before the user is signed in: Triggers after a user's credentials are verified, butbeforeFirebase Authentication returns an ID token to your client app. Ifyour app uses multi-factor authentication, the functiontriggers after the user verifies their second factor. Note that creating a newuser also triggers both these events.
Before sending an email (Node.js only): Triggers before an email (for example, a sign-in or password reset email) is sent to a user.
Before sending an SMS message (Node.js only): Triggers before anSMS Message is sent to a user, for cases such as multifactor authentication.
Keep the following in mind when using blocking functions:
Your function must respond within 7 seconds. After 7 seconds,Firebase Authentication returns an error, and the client operation fails.
HTTP response codes other than
200are passed to your client apps. Ensureyour client code handles any errors your function can return.Functions apply to all users in your project, including any contained in atenant.Firebase Authentication provides information about users to your function, includingany tenants they belong to, so you can respond accordingly.
Linking another identity provider to an account re-triggers any registered
beforeUserSignedInfunctions.Anonymous and custom authentication do not trigger blocking functions.
Deploy a blocking function
To insert your custom code into the user authentication flows, deploy blockingfunctions. Once your blocking functions are deployed, your custom code mustcomplete successfully for authentication and user creation to succeed.
You deploy a blocking function the same way as you deploy any function.(see theCloud FunctionsGetting started pagefor details). In summary:
Write a function that handles the targeted event.
For example, to get started, you can add a no-op function like thefollowing to your source:
Node.js
import{beforeUserCreated,}from"firebase-functions/v2/identity";exportconstbeforecreated=beforeUserCreated((event)=>{// TODOreturn;});Python
@identity_fn.before_user_created()defcreated_noop(event:identity_fn.AuthBlockingEvent)->identity_fn.BeforeCreateResponse|None:returnThe above example has omitted the implementation of custom auth logic. Seethe following sections to learn how to implement your blocking functions andCommon scenarios for specific examples.
Deploy your functions using theFirebase CLI:
firebasedeploy--onlyfunctionsYou must re-deploy your functions each time you update them.
Getting user and context information
Blocking events provide anAuthBlockingEventobject that contains information about the user signing in. Use these valuesin your code to determine whether to allow an operation to proceed.
The object contains the following properties:
| Name | Description | Example |
|---|---|---|
locale | The application locale. You can set the locale using the client SDK, or by passing the locale header in the REST API. | fr orsv-SE |
ipAddress | The IP address of the device the end user is registering or signing in from. | 114.14.200.1 |
userAgent | The user agent triggering the blocking function. | Mozilla/5.0 (X11; Linux x86_64) |
eventId | The event's unique identifier. | rWsyPtolplG2TBFoOkkgyg |
eventType | The event type. This provides information on the event name, such asbeforeSignIn orbeforeCreate, and the associated sign-in method used, like Google or email/password. | providers/cloud.auth/eventTypes/user.beforeSignIn:password |
authType | AlwaysUSER. | USER |
resource | TheFirebase Authentication project or tenant. | projects/project-id/tenants/tenant-id |
timestamp | The time the event was triggered, formatted as anRFC 3339 string. | Tue, 23 Jul 2019 21:10:57 GMT |
additionalUserInfo | An object containing information about the user. | AdditionalUserInfo |
credential | An object containing information about the user's credential. | AuthCredential |
Blocking registration or sign-in
To block a registration or sign-in attempt, throw anHttpsError in yourfunction. For example:
Node.js
import{HttpsError}from"firebase-functions/v2/identity";thrownewHttpsError('invalid-argument');Python
raisehttps_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT)You can also specify a custom error message:
Node.js
thrownewHttpsError('permission-denied','Unauthorized request origin!');Python
raisehttps_fn.HttpsError(code=https_fn.FunctionsErrorCode.PERMISSION_DENIED,message="Unauthorized request origin!")The following example shows how to block users who are not within a specificdomain from registering for your app:
Node.js
exportconstbeforecreated=beforeUserCreated((event)=>{constuser=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')){thrownewHttpsError('invalid-argument',"Unauthorized email");}});Python
# Block account creation with any non-acme email address.@identity_fn.before_user_created()defvalidatenewuser(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.ifuser.emailisNoneor"@acme.com"notinuser.email:# Return None so that Firebase Auth rejects the account creation.raisehttps_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,message="Unauthorized email")Regardless of whether you use a default or custom message,Cloud Functions wraps the error and returns it to the client as aninternal error. For example:
Node.js
thrownewHttpsError('invalid-argument',"Unauthorized email");Python
# Only users of a specific domain can sign up.ifuser.emailisNoneor"@acme.com"notinuser.email:# Return None so that Firebase Auth rejects the account creation.raisehttps_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,message="Unauthorized email")Your app should catch the error, and handle it accordingly. For example:
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';constauth=getAuth();try{constresult=awaitcreateUserWithEmailAndPassword(auth)constidTokenResult=awaitresult.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.}}Modifying a user
Instead of blocking a registration or sign-in attempt, you can allow theoperation to continue, but modify theUser object that is saved toFirebase Authentication's database and returned to the client.
To modify a user, return an object from your event handler containing thefields to modify. You can modify the following fields:
displayNamedisabledemailVerifiedphotoUrlcustomClaimssessionClaims(beforeUserSignedInonly)
With the exception ofsessionClaims, all modified fields are saved toFirebase Authentication's database, which means they are included on the responsetoken and persist between user sessions.
The following example shows how to set a default display name:
Node.js
exportconstbeforecreated=beforeUserCreated((event)=>{return{// If no display name is provided, set it to "Guest".displayName:event.data.displayName||'Guest'};});Python
@identity_fn.before_user_created()defsetdefaultname(event:identity_fn.AuthBlockingEvent)->identity_fn.BeforeCreateResponse|None:returnidentity_fn.BeforeCreateResponse(# If no display name is provided, set it to "Guest".display_name=event.data.display_nameifevent.data.display_nameisnotNoneelse"Guest")If you register an event handler for bothbeforeUserCreated andbeforeUserSignedIn,note thatbeforeUserSignedIn executes afterbeforeUserCreated. User fields updated inbeforeUserCreated are visible inbeforeUserSignedIn. If you set a field other thansessionClaims in both event handlers, the value set inbeforeUserSignedInoverwrites the value set inbeforeUserCreated. ForsessionClaims only, they arepropagated to the current session's token claims, but are not persisted orstored in the database.
For example, if anysessionClaims are set,beforeUserSignedIn will return themwith anybeforeUserCreated claims, and they will be merged. When they're merged, ifasessionClaims key matches a key incustomClaims, the matchingcustomClaims will be overwritten in the token claims by thesessionClaimskey. However, the overwittencustomClaims key will still be persisted in thedatabase for future requests.
Supported OAuth credentials and data
You can pass OAuth credentials and data to blocking functions from variousidentity providers. The following table shows what credentials and data aresupported for each identity provider:
| Identity Provider | ID Token | Access Token | Expiration Time | Token Secret | Refresh Token | Sign-in Claims |
|---|---|---|---|---|---|---|
| Yes | Yes | Yes | No | Yes | No | |
| No | Yes | Yes | No | No | No | |
| No | Yes | No | Yes | No | No | |
| GitHub | No | Yes | No | No | No | No |
| Microsoft | Yes | Yes | Yes | No | Yes | No |
| No | Yes | Yes | No | No | No | |
| Yahoo | Yes | Yes | Yes | No | Yes | No |
| Apple | Yes | Yes | Yes | No | Yes | No |
| SAML | No | No | No | No | No | Yes |
| OIDC | Yes | Yes | Yes | No | Yes | Yes |
OAuth tokens
To use an ID token, access token, or refresh token in a blocking function,you must first select thecheckbox on theBlocking functions page of theFirebase console.
Refresh tokens will not be returned by any identity providers when signing indirectly with an OAuth credential, such as an ID token or access token. In thissituation, the same client-side OAuth credential will be passed to the blockingfunction.
The following sections describe each identity provider types and their supportedcredentials and data.
Generic OIDC providers
When a user signs in with a generic OIDC provider, the following credentialswill be passed:
- ID token: Provided if the
id_tokenflow is selected. - Access token: Provided if the code flow is selected. Note that the codeflow is only currently supported via the REST API.
- Refresh token: Provided if the
offline_accessscopeis selected.
Example:
constprovider=newfirebase.auth.OAuthProvider('oidc.my-provider');provider.addScope('offline_access');firebase.auth().signInWithPopup(provider);When a user signs in with Google, the following credentials will be passed:
- ID token
- Access token
- Refresh token: Only provided if the following custom parameters arerequested:
access_type=offlineprompt=consent, if the user previously consented and nonew scope was requested
Example:
import{getAuth,signInWithPopup,GoogleAuthProvider}from'firebase/auth';constauth=getAuth();constprovider=newGoogleAuthProvider();provider.setCustomParameters({'access_type':'offline','prompt':'consent'});signInWithPopup(auth,provider);Learn more aboutGoogle refresh tokens.
When a user signs in with Facebook, the following credential will be passed:
- Access token: An access token is returned that can be exchanged foranother access token. Learn more about the different types ofaccess tokenssupported by Facebook and how you can exchange them forlong-lived tokens.
GitHub
When a user signs in with GitHub, the following credential will be passed:
- Access token: Does not expire unless revoked.
Microsoft
When a user signs in with Microsoft, the following credentials will be passed:
- ID token
- Access token
- Refresh token: Passed to the blocking function if the
offline_accessscopeis selected.
Example:
import{getAuth,signInWithPopup,OAuthProvider}from'firebase/auth';constauth=getAuth();constprovider=newOAuthProvider('microsoft.com');provider.addScope('offline_access');signInWithPopup(auth,provider);Yahoo
When a user signs in with Yahoo, the following credentials will be passedwithout any custom parameters or scopes:
- ID token
- Access token
- Refresh token
When a user signs in with LinkedIn, the following credential will be passed:
- Access token
Apple
When a user signs in with Apple, the following credentials will be passedwithout any custom parameters or scopes:
- ID token
- Access token
- Refresh token
Common scenarios
The following examples demonstrate some common use cases for blocking functions:
Only allowing registration from a specific domain
The following example shows how to prevent users who aren't part of theexample.com domain from registering with your app:
Node.js
exportconstbeforecreated=beforeUserCreated((event)=>{constuser=event.data;if(!user?.email?.includes('@example.com')){thrownewHttpsError('invalid-argument','Unauthorized email');}});Python
@identity_fn.before_user_created()defvalidatenewuser(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.ifuser.emailisNoneor"@example.com"notinuser.email:# Return None so that Firebase Auth rejects the account creation.raisehttps_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,message="Unauthorized email",)Blocking users with unverified emails from registering
The following example shows how to prevent users with unverified emails fromregistering with your app:
Node.js
exportconstbeforecreated=beforeUserCreated((event)=>{constuser=event.data;if(user.email &&!user.emailVerified){thrownewHttpsError('invalid-argument','Unverified email');}});Python
@identity_fn.before_user_created()defrequireverified(event:identity_fn.AuthBlockingEvent)->identity_fn.BeforeCreateResponse|None:ifevent.data.emailisnotNoneandnotevent.data.email_verified:raisehttps_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,message="You must register using a trusted provider.")Treating certain identity provider emails as verified
The following example shows how to treat user emails from certain identityproviders as verified:
Node.js
exportconstbeforecreated=beforeUserCreated((event)=>{constuser=event.data;if(user.email &&!user.emailVerified &&event.eventType.includes(':facebook.com')){return{emailVerified:true,};}});Python
@identity_fn.before_user_created()defmarkverified(event:identity_fn.AuthBlockingEvent)->identity_fn.BeforeCreateResponse|None:ifevent.data.emailisnotNoneand"@facebook.com"inevent.data.email:returnidentity_fn.BeforeSignInResponse(email_verified=True)Blocking sign-in from certain IP addresses
The following example shows how to block sign-in from certain IP address ranges:
Node.js
exportconstbeforesignedin=beforeUserSignedIn((event)=>{if(isSuspiciousIpAddress(event.ipAddress)){thrownewHttpsError('permission-denied','Unauthorized access!');}});Python
@identity_fn.before_user_signed_in()defipban(event:identity_fn.AuthBlockingEvent)->identity_fn.BeforeSignInResponse|None:ifis_suspicious(event.ip_address):raisehttps_fn.HttpsError(code=https_fn.FunctionsErrorCode.PERMISSION_DENIED,message="IP banned.")Setting custom and session claims
The following example shows how to set custom and session claims:
Node.js
exportconstbeforecreated=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,},};}});exportconstbeforesignin=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()defsetemployeeid(event:identity_fn.AuthBlockingEvent)->identity_fn.BeforeCreateResponse|None:if(event.credentialisnotNoneandevent.credential.claimsisnotNoneandevent.credential.provider_id=="saml.my-provider-id"):returnidentity_fn.BeforeCreateResponse(custom_claims={"eid":event.credential.claims["employeeid"]})@identity_fn.before_user_signed_in()defcopyclaimstosession(event:identity_fn.AuthBlockingEvent)->identity_fn.BeforeSignInResponse|None:if(event.credentialisnotNoneandevent.credential.claimsisnotNoneandevent.credential.provider_id=="saml.my-provider-id"):returnidentity_fn.BeforeSignInResponse(session_claims={"role":event.credential.claims["role"],"groups":event.credential.claims["groups"]})Tracking IP addresses to monitor suspicious activity
You can prevent token theft by tracking the IP address a user signs in from,and comparing it to the IP address on subsequent requests. If the requestappears suspicious — for example, the IPs are from from different geographicalregions — you can ask the user to sign in again.
Use session claims to track the IP address the user signs in with:
Node.js
exportconstbeforesignedin=beforeUserSignedIn((event)=>{return{sessionClaims:{signInIpAddress:event.ipAddress,},};});Python
@identity_fn.before_user_signed_in()deflogip(event:identity_fn.AuthBlockingEvent)->identity_fn.BeforeSignInResponse|None:returnidentity_fn.BeforeSignInResponse(session_claims={"signInIpAddress":event.ip_address})When a user attempts to access resources that require authentication withFirebase Authentication, compare the IP address in the request with the IP usedto sign in:
Node.js
app.post('/getRestrictedData',(req,res)=>{// Get the ID token passed.constidToken=req.body.idToken;// Verify the ID token, check if revoked and decode its payload.admin.auth().verifyIdToken(idToken,true).then((claims)=>{// Get request IP addressconstrequestIpAddress=req.connection.remoteAddress;// Get sign-in IP address.constsignInIpAddress=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
fromfirebase_adminimportauth,initialize_appimportflaskinitialize_app()flask_app=flask.Flask(__name__)@flask_app.post()defget_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:returnflask.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.ifis_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"])returnflask.Response(status=401,response="Unauthorized access. Sign in again!")else:# Access is valid. Try to return data.returndata_from_claims(claims)
Screening user photos
The following example shows how to sanitize users' profile photos:
Node.js
exportconstbeforecreated=beforeUserCreated((event)=>{constuser=event.data;if(user.photoURL){returnisPhotoAppropriate(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()defsanitizeprofilephoto(event:identity_fn.AuthBlockingEvent)->identity_fn.BeforeCreateResponse|None:ifevent.data.photo_urlisnotNone:score=analyze_photo_with_ml(event.data.photo_url)ifscore >THRESHOLD:returnidentity_fn.BeforeCreateResponse(photo_url=PLACEHOLDER_URL)To learn more about how to detect and sanitize images, see theCloud Vision documentation.
Accessing a user's identity provider OAuth credentials
The following example demonstrates how to obtain a refresh token for a userthat signed in with Google, and use it to call the Google Calendar APIs. Therefresh token is stored for offline access.
Node.js
const{OAuth2Client}=require('google-auth-library');const{google}=require('googleapis');// ...// Initialize Google OAuth client.constkeys=require('./oauth2.keys.json');constoAuth2Client=newOAuth2Client(keys.web.client_id,keys.web.client_secret);exportconstbeforecreated=beforeUserCreated((event)=>{constuser=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).returnsaveUserRefreshToken(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.returnnewPromise((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,});constcalendar=google.calendar('v3');// Setup Onboarding event on user's calendar.constevent={/** ... */};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()defsavegoogletoken(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 """ifevent.credentialisnotNoneandevent.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,"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)Overriding reCAPTCHA Enterprise verdict for user operation
The following example shows how to override a reCAPTCHA Enterprise verdict for supported user flows.
Refer toEnable reCAPTCHA Enterprise to learn more about integrating reCAPTCHA Enterprise with Firebase Authentication.
Blocking functions can be used to allow or block flows based on custom factors, thereby overriding the result provided by 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.5if(event.additionalUserInfo.recaptchaScore >0.5){return{recaptchaActionOverride:'ALLOW',};}// Block all others.return{recaptchaActionOverride:'BLOCK',}});Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2025-12-17 UTC.