@@ -36,6 +36,8 @@ class _TypeCheckers {
3636 TypeChecker .fromRuntime (ff.AppDistributionNamespace );
3737 static final performanceNamespace =
3838 TypeChecker .fromRuntime (ff.PerformanceNamespace );
39+ static final identityNamespace =
40+ TypeChecker .fromRuntime (ff.IdentityNamespace );
3941}
4042
4143/// The main builder that generates functions.yaml.
@@ -179,6 +181,11 @@ class _FirebaseFunctionsVisitor extends RecursiveAstVisitor<void> {
179181 _extractPerformanceAlertFunction (node, methodName);
180182 }
181183
184+ // Check for Identity function declarations
185+ if (target != null && _isIdentityNamespace (target)) {
186+ _extractIdentityFunction (node, methodName);
187+ }
188+
182189 // Check for parameter definitions (top-level function calls with no target)
183190 if (target == null && _isParamDefinition (methodName)) {
184191 _extractParameterFromMethod (node, methodName);
@@ -325,6 +332,13 @@ class _FirebaseFunctionsVisitor extends RecursiveAstVisitor<void> {
325332 return _TypeCheckers .performanceNamespace.isExactlyType (staticType);
326333 }
327334
335+ /// Checks if the target is firebase.identity.
336+ bool _isIdentityNamespace (Expression target) {
337+ final staticType = target.staticType;
338+ if (staticType == null ) return false ;
339+ return _TypeCheckers .identityNamespace.isExactlyType (staticType);
340+ }
341+
328342 /// Checks if this is a parameter definition function.
329343 bool _isParamDefinition (String name) =>
330344 name == 'defineString' ||
@@ -585,6 +599,60 @@ class _FirebaseFunctionsVisitor extends RecursiveAstVisitor<void> {
585599 );
586600 }
587601
602+ /// Extracts an Identity function declaration.
603+ void _extractIdentityFunction (MethodInvocation node, String methodName) {
604+ // Map method names to event types
605+ final eventType = switch (methodName) {
606+ 'beforeUserCreated' => 'beforeCreate' ,
607+ 'beforeUserSignedIn' => 'beforeSignIn' ,
608+ 'beforeEmailSent' => 'beforeSendEmail' ,
609+ 'beforeSmsSent' => 'beforeSendSms' ,
610+ _ => null ,
611+ };
612+
613+ if (eventType == null ) return ;
614+
615+ // Extract options if present
616+ final optionsArg = _findNamedArg (node, 'options' );
617+ bool ? idToken;
618+ bool ? accessToken;
619+ bool ? refreshToken;
620+
621+ if (optionsArg is InstanceCreationExpression ) {
622+ idToken = _extractBoolField (optionsArg, 'idToken' );
623+ accessToken = _extractBoolField (optionsArg, 'accessToken' );
624+ refreshToken = _extractBoolField (optionsArg, 'refreshToken' );
625+ }
626+
627+ // Function name is the event type
628+ final functionName = eventType;
629+
630+ endpoints[functionName] = _EndpointSpec (
631+ name: functionName,
632+ type: 'blocking' ,
633+ blockingEventType: eventType,
634+ idToken: idToken,
635+ accessToken: accessToken,
636+ refreshToken: refreshToken,
637+ options: optionsArg is InstanceCreationExpression ? optionsArg : null ,
638+ variableToParamName: _variableToParamName,
639+ );
640+ }
641+
642+ /// Extracts a boolean field from an InstanceCreationExpression.
643+ bool ? _extractBoolField (InstanceCreationExpression node, String fieldName) {
644+ final arg = node.argumentList.arguments
645+ .whereType <NamedExpression >()
646+ .where ((e) => e.name.label.name == fieldName)
647+ .map ((e) => e.expression)
648+ .firstOrNull;
649+
650+ if (arg is BooleanLiteral ) {
651+ return arg.value;
652+ }
653+ return null ;
654+ }
655+
588656 /// Extracts alert type value from an expression.
589657 String ? _extractAlertTypeValue (Expression expression) {
590658 if (expression is InstanceCreationExpression ) {
@@ -816,11 +884,15 @@ class _EndpointSpec {
816884 this .instance,
817885 this .alertType,
818886 this .appId,
887+ this .blockingEventType,
888+ this .idToken,
889+ this .accessToken,
890+ this .refreshToken,
819891 this .options,
820892 this .variableToParamName = const {},
821893 });
822894 final String name;
823- // 'https', 'callable', 'pubsub', 'firestore', 'database', 'alert'
895+ // 'https', 'callable', 'pubsub', 'firestore', 'database', 'alert', 'blocking'
824896 final String type;
825897 final String ? topic; // For Pub/Sub functions
826898 final String ? firestoreEventType; // For Firestore: onDocumentCreated, etc.
@@ -832,6 +904,10 @@ class _EndpointSpec {
832904 final String ? instance; // For Database: database instance or '*'
833905 final String ? alertType; // For Alerts: crashlytics.newFatalIssue, etc.
834906 final String ? appId; // For Alerts: optional app ID filter
907+ final String ? blockingEventType; // For Identity: beforeCreate, etc.
908+ final bool ? idToken; // For Identity: pass ID token
909+ final bool ? accessToken; // For Identity: pass access token
910+ final bool ? refreshToken; // For Identity: pass refresh token
835911 final InstanceCreationExpression ? options;
836912 final Map <String , String > variableToParamName;
837913
@@ -1375,6 +1451,13 @@ String _generateYaml(
13751451 buffer.writeln ('requiredAPIs:' );
13761452 buffer.writeln (' - api: "cloudfunctions.googleapis.com"' );
13771453 buffer.writeln (' reason: "Required for Cloud Functions"' );
1454+ // Add identitytoolkit API if there are blocking functions
1455+ final hasBlockingFunctions =
1456+ endpoints.values.any ((e) => e.type == 'blocking' );
1457+ if (hasBlockingFunctions) {
1458+ buffer.writeln (' - api: "identitytoolkit.googleapis.com"' );
1459+ buffer.writeln (' reason: "Needed for auth blocking functions"' );
1460+ }
13781461 buffer.writeln ();
13791462
13801463 // Generate endpoints section
@@ -1578,6 +1661,30 @@ String _generateYaml(
15781661 buffer.writeln (' appid: "${endpoint .appId }"' );
15791662 }
15801663 buffer.writeln (' retry: false' );
1664+ } else if (endpoint.type == 'blocking' &&
1665+ endpoint.blockingEventType != null ) {
1666+ buffer.writeln (' blockingTrigger:' );
1667+ buffer.writeln (
1668+ ' eventType: "providers/cloud.auth/eventTypes/user.${endpoint .blockingEventType }"' ,
1669+ );
1670+
1671+ // Only include token options for beforeCreate and beforeSignIn
1672+ final isAuthEvent = endpoint.blockingEventType == 'beforeCreate' ||
1673+ endpoint.blockingEventType == 'beforeSignIn' ;
1674+ if (isAuthEvent) {
1675+ buffer.writeln (' options:' );
1676+ if (endpoint.idToken ?? false ) {
1677+ buffer.writeln (' idToken: true' );
1678+ }
1679+ if (endpoint.accessToken ?? false ) {
1680+ buffer.writeln (' accessToken: true' );
1681+ }
1682+ if (endpoint.refreshToken ?? false ) {
1683+ buffer.writeln (' refreshToken: true' );
1684+ }
1685+ } else {
1686+ buffer.writeln (' options: {}' );
1687+ }
15811688 }
15821689 }
15831690 }
0 commit comments