@@ -36,8 +36,20 @@ internal static class FirebaseInterops {
3636 // The header used by the AppCheck token.
3737 private const string appCheckHeader = "X-Firebase-AppCheck" ;
3838
39+ // The various Auth types needed to retrieve the token, cached via reflection on startup.
40+ private static Type _authType ;
41+ private static MethodInfo _authGetAuthMethod ;
42+ private static PropertyInfo _authCurrentUserProperty ;
43+ private static MethodInfo _userTokenAsyncMethod ;
44+ private static PropertyInfo _userTokenTaskResultProperty ;
45+ // Used to determine if the Auth reflection initialized successfully, and should work.
46+ private static bool _authReflectionInitialized = false ;
47+ // The header used by the AppCheck token.
48+ private const string authHeader = "Authorization" ;
49+
3950 static FirebaseInterops ( ) {
4051 InitializeAppCheckReflection ( ) ;
52+ InitializeAuthReflection ( ) ;
4153 }
4254
4355 private static void LogError ( string message ) {
@@ -150,24 +162,144 @@ internal static async Task<string> GetAppCheckTokenAsync(FirebaseApp firebaseApp
150162 return null ;
151163 }
152164
165+ // Cache the various types and methods needed for Auth token retrieval.
166+ private static void InitializeAuthReflection ( ) {
167+ const string firebaseAuthTypeName = "Firebase.Auth.FirebaseAuth, Firebase.Auth" ;
168+ const string getTokenMethodName = "TokenAsync" ;
169+
170+ try {
171+ // Set this to false, to allow easy failing out via return.
172+ _authReflectionInitialized = false ;
173+
174+ _authType = Type . GetType ( firebaseAuthTypeName ) ;
175+ if ( _authType == null ) {
176+ // Auth assembly likely not present, fine to skip
177+ return ;
178+ }
179+
180+ // Get the static method GetAuth(FirebaseApp app):
181+ _authGetAuthMethod = _authType . GetMethod (
182+ "GetAuth" , BindingFlags . Static | BindingFlags . Public , null ,
183+ new Type [ ] { typeof ( FirebaseApp ) } , null ) ;
184+ if ( _authGetAuthMethod == null ) {
185+ LogError ( "Could not find FirebaseAuth.GetAuth method via reflection." ) ;
186+ return ;
187+ }
188+
189+ // Get the CurrentUser property from FirebaseAuth instance
190+ _authCurrentUserProperty = _authType . GetProperty ( "CurrentUser" , BindingFlags . Instance | BindingFlags . Public ) ;
191+ if ( _authCurrentUserProperty == null ) {
192+ LogError ( "Could not find FirebaseAuth.CurrentUser property via reflection." ) ;
193+ return ;
194+ }
195+
196+ // This should be FirebaseUser type
197+ Type userType = _authCurrentUserProperty . PropertyType ;
198+
199+ // Get the TokenAsync(bool) method from FirebaseUser
200+ _userTokenAsyncMethod = userType . GetMethod (
201+ getTokenMethodName , BindingFlags . Instance | BindingFlags . Public , null ,
202+ new Type [ ] { typeof ( bool ) } , null ) ;
203+ if ( _userTokenAsyncMethod == null ) {
204+ LogError ( $ "Could not find FirebaseUser.{ getTokenMethodName } (bool) method via reflection.") ;
205+ return ;
206+ }
207+
208+ // The return type is Task<string>
209+ Type tokenTaskType = _userTokenAsyncMethod . ReturnType ;
210+
211+ // Get the Result property from Task<string>
212+ _userTokenTaskResultProperty = tokenTaskType . GetProperty ( "Result" ) ;
213+ if ( _userTokenTaskResultProperty == null ) {
214+ LogError ( "Could not find Result property on Auth token Task." ) ;
215+ return ;
216+ }
217+
218+ // Check if Result property is actually a string
219+ if ( _userTokenTaskResultProperty . PropertyType != typeof ( string ) ) {
220+ LogError ( "Auth token Task's Result property is not a string, " +
221+ $ "but is { _userTokenTaskResultProperty . PropertyType } ") ;
222+ return ;
223+ }
224+
225+ _authReflectionInitialized = true ;
226+ } catch ( Exception e ) {
227+ LogError ( $ "Exception during static initialization of Auth reflection in FirebaseInterops: { e } ") ;
228+ _authReflectionInitialized = false ;
229+ }
230+ }
231+
232+ // Gets the Auth Token, assuming there is one. Otherwise, returns null.
233+ internal static async Task < string > GetAuthTokenAsync ( FirebaseApp firebaseApp ) {
234+ // If Auth reflection failed for any reason, nothing to do.
235+ if ( ! _authReflectionInitialized ) {
236+ return null ;
237+ }
238+
239+ try {
240+ // Get the FirebaseAuth instance for the given FirebaseApp.
241+ object authInstance = _authGetAuthMethod . Invoke ( null , new object [ ] { firebaseApp } ) ;
242+ if ( authInstance == null ) {
243+ LogError ( "Failed to get FirebaseAuth instance via reflection." ) ;
244+ return null ;
245+ }
246+
247+ // Get the CurrentUser property
248+ object currentUser = _authCurrentUserProperty . GetValue ( authInstance ) ;
249+ if ( currentUser == null ) {
250+ // No user logged in, so no token
251+ return null ;
252+ }
253+
254+ // Invoke TokenAsync(false) - returns a Task<string>
255+ object taskObject = _userTokenAsyncMethod . Invoke ( currentUser , new object [ ] { false } ) ;
256+ if ( taskObject is not Task tokenTask ) {
257+ LogError ( "Invoking TokenAsync did not return a Task." ) ;
258+ return null ;
259+ }
260+
261+ // Await the task to get the token result
262+ await tokenTask ;
263+
264+ // Check for exceptions in the task
265+ if ( tokenTask . IsFaulted ) {
266+ LogError ( $ "Error getting Auth token: { tokenTask . Exception } ") ;
267+ return null ;
268+ }
269+
270+ // Get the Result property (which is the string token)
271+ return _userTokenTaskResultProperty . GetValue ( tokenTask ) as string ;
272+ } catch ( Exception e ) {
273+ // Log any exceptions during the reflection/invocation process
274+ LogError ( $ "An error occurred while trying to fetch Auth token: { e } ") ;
275+ }
276+ return null ;
277+ }
278+
153279 // Adds the other Firebase tokens to the HttpRequest, as available.
154280 internal static async Task AddFirebaseTokensAsync ( HttpRequestMessage request , FirebaseApp firebaseApp ) {
155- string tokenString = await GetAppCheckTokenAsync ( firebaseApp ) ;
281+ string appCheckToken = await GetAppCheckTokenAsync ( firebaseApp ) ;
282+ if ( ! string . IsNullOrEmpty ( appCheckToken ) ) {
283+ request . Headers . Add ( appCheckHeader , appCheckToken ) ;
284+ }
156285
157- // Add the header if the token is valid
158- if ( ! string . IsNullOrEmpty ( tokenString ) ) {
159- request . Headers . Add ( appCheckHeader , tokenString ) ;
286+ string authToken = await GetAuthTokenAsync ( firebaseApp ) ;
287+ if ( ! string . IsNullOrEmpty ( authToken ) ) {
288+ request . Headers . Add ( authHeader , $ "Firebase { authToken } " ) ;
160289 }
161290 }
162291
163292 // Adds the other Firebase tokens to the WebSocket, as available.
164293 internal static async Task AddFirebaseTokensAsync ( ClientWebSocket socket , FirebaseApp firebaseApp ) {
165- string tokenString = await GetAppCheckTokenAsync ( firebaseApp ) ;
294+ string appCheckToken = await GetAppCheckTokenAsync ( firebaseApp ) ;
295+ if ( ! string . IsNullOrEmpty ( appCheckToken ) ) {
296+ socket . Options . SetRequestHeader ( appCheckHeader , appCheckToken ) ;
297+ }
166298
167- // Add the header if the token is valid
168- if ( ! string . IsNullOrEmpty ( tokenString ) ) {
169- socket . Options . SetRequestHeader ( appCheckHeader , tokenString ) ;
170- }
299+ string authToken = await GetAuthTokenAsync ( firebaseApp ) ;
300+ if ( ! string . IsNullOrEmpty ( authToken ) ) {
301+ socket . Options . SetRequestHeader ( authHeader , $ "Firebase { authToken } " ) ;
302+ }
171303 }
172304
173305}
0 commit comments