@@ -24,12 +24,21 @@ public sealed class FirebaseAppCheck {
2424 // The C++ object that this wraps.
2525 private AppCheckInternal appCheckInternal ;
2626
27- private static Dictionary < FirebaseApp , FirebaseAppCheck > appCheckMap =
28- new Dictionary < FirebaseApp , FirebaseAppCheck > ( ) ;
27+ // Use the FirebaseApp's name instead of the App itself, to not
28+ // keep it alive unnecessarily.
29+ private static Dictionary < string , FirebaseAppCheck > appCheckMap =
30+ new Dictionary < string , FirebaseAppCheck > ( ) ;
2931 // The user provided Factory.
3032 private static IAppCheckProviderFactory appCheckFactory ;
31- private static Dictionary < FirebaseApp , IAppCheckProvider > providerMap =
32- new Dictionary < FirebaseApp , IAppCheckProvider > ( ) ;
33+ private static Dictionary < string , IAppCheckProvider > providerMap =
34+ new Dictionary < string , IAppCheckProvider > ( ) ;
35+
36+ // Function for C++ to call when it needs to fetch a Token.
37+ private static AppCheckUtil . GetTokenFromCSharpDelegate getTokenDelegate =
38+ new AppCheckUtil . GetTokenFromCSharpDelegate ( GetTokenFromCSharpMethod ) ;
39+ // Function for C++ to call when the Token changes.
40+ private static AppCheckUtil . TokenChangedDelegate tokenChangedDelegate =
41+ new AppCheckUtil . TokenChangedDelegate ( TokenChangedMethod ) ;
3342
3443 // Make the constructor private, since users aren't meant to make it.
3544 private FirebaseAppCheck ( AppCheckInternal internalObject ) {
@@ -55,11 +64,10 @@ public static FirebaseAppCheck DefaultInstance {
5564 /// {@link FirebaseApp} instance.
5665 public static FirebaseAppCheck GetInstance ( FirebaseApp app ) {
5766 FirebaseAppCheck result ;
58- if ( ! appCheckMap . TryGetValue ( app , out result ) ) {
67+ if ( ! appCheckMap . TryGetValue ( app . Name , out result ) ) {
5968 AppCheckInternal internalObject = AppCheckInternal . GetInstance ( app ) ;
6069 result = new FirebaseAppCheck ( internalObject ) ;
61- appCheckMap [ app ] = result ;
62- // TODO(amaurice): Logic to remove from map when App is destroyed?
70+ appCheckMap [ app . Name ] = result ;
6371 }
6472 return result ;
6573 }
@@ -79,8 +87,20 @@ public static FirebaseAppCheck GetInstance(FirebaseApp app) {
7987 ///
8088 /// This method should be called before initializing the Firebase App.
8189 public static void SetAppCheckProviderFactory ( IAppCheckProviderFactory factory ) {
90+ if ( appCheckFactory == factory ) return ;
91+
8292 appCheckFactory = factory ;
83- // TODO(amaurice): Clear the provider map when the factory changes?
93+ // Clear out the Providers that were previously made. When future calls to
94+ // GetToken fails to find a provider in the map, it will use the new factory
95+ // to create a new provider.
96+ providerMap . Clear ( ) ;
97+
98+ // Register the callback for C++ SDK to use that will reach this factory.
99+ if ( factory == null ) {
100+ AppCheckUtil . SetGetTokenCallback ( null ) ;
101+ } else {
102+ AppCheckUtil . SetGetTokenCallback ( getTokenDelegate ) ;
103+ }
84104 }
85105
86106 /// Sets the {@code isTokenAutoRefreshEnabled} flag.
@@ -95,11 +115,93 @@ public void SetTokenAutoRefreshEnabled(bool isTokenAutoRefreshEnabled) {
95115 public System . Threading . Tasks . Task < AppCheckToken >
96116 GetAppCheckTokenAsync ( bool forceRefresh ) {
97117 ThrowIfNull ( ) ;
98- throw new NotImplementedException ( ) ;
118+ return appCheckInternal . GetAppCheckTokenAsync ( forceRefresh ) . ContinueWith ( task => {
119+ if ( task . IsFaulted ) {
120+ throw task . Exception ;
121+ }
122+ AppCheckTokenInternal tokenInternal = task . Result ;
123+ return AppCheckToken . FromAppCheckTokenInternal ( tokenInternal ) ;
124+ } ) ;
99125 }
100126
101127 /// Called on the client when an AppCheckToken is created or changed.
102- public System . EventHandler < TokenChangedEventArgs > TokenChanged ;
128+ private event EventHandler < TokenChangedEventArgs > TokenChangedImpl ;
129+ public event EventHandler < TokenChangedEventArgs > TokenChanged {
130+ add {
131+ ThrowIfNull ( ) ;
132+ // If this is the first listener, hook into C++.
133+ if ( TokenChangedImpl == null ||
134+ TokenChangedImpl . GetInvocationList ( ) . Length == 0 ) {
135+ AppCheckUtil . SetTokenChangedCallback ( appCheckInternal , tokenChangedDelegate ) ;
136+ }
137+
138+ TokenChangedImpl += value ;
139+ }
140+ remove {
141+ ThrowIfNull ( ) ;
142+ TokenChangedImpl -= value ;
143+
144+ // If that was the last listener, remove the C++ hooks.
145+ if ( TokenChangedImpl == null ||
146+ TokenChangedImpl . GetInvocationList ( ) . Length == 0 ) {
147+ AppCheckUtil . SetTokenChangedCallback ( appCheckInternal , null ) ;
148+ }
149+ }
150+ }
151+
152+ internal void OnTokenChanged ( AppCheckToken token ) {
153+ EventHandler < TokenChangedEventArgs > handler = TokenChangedImpl ;
154+ if ( handler != null ) {
155+ handler ( this , new TokenChangedEventArgs ( ) { Token = token } ) ;
156+ }
157+ }
158+
159+ [ MonoPInvokeCallback ( typeof ( AppCheckUtil . GetTokenFromCSharpDelegate ) ) ]
160+ private static void GetTokenFromCSharpMethod ( string appName , int key ) {
161+ if ( appCheckFactory == null ) {
162+ AppCheckUtil . FinishGetTokenCallback ( key , "" , 0 ,
163+ ( int ) AppCheckError . InvalidConfiguration ,
164+ "Missing IAppCheckProviderFactory." ) ;
165+ }
166+ FirebaseApp app = FirebaseApp . GetInstance ( appName ) ;
167+ if ( app == null ) {
168+ AppCheckUtil . FinishGetTokenCallback ( key , "" , 0 ,
169+ ( int ) AppCheckError . Unknown ,
170+ "Unable to find App with name: " + appName ) ;
171+ }
172+ IAppCheckProvider provider ;
173+ if ( ! providerMap . TryGetValue ( app . Name , out provider ) ) {
174+ provider = appCheckFactory . CreateProvider ( app ) ;
175+ if ( provider == null ) {
176+ AppCheckUtil . FinishGetTokenCallback ( key , "" , 0 ,
177+ ( int ) AppCheckError . InvalidConfiguration ,
178+ "Failed to create IAppCheckProvider for App: " + appName ) ;
179+ }
180+ providerMap [ app . Name ] = provider ;
181+ }
182+ provider . GetTokenAsync ( ) . ContinueWith ( task => {
183+ if ( task . IsFaulted ) {
184+ AppCheckUtil . FinishGetTokenCallback ( key , "" , 0 ,
185+ ( int ) AppCheckError . Unknown ,
186+ "Provider returned an Exception: " + task . Exception ) ;
187+ } else {
188+ AppCheckToken token = task . Result ;
189+ AppCheckUtil . FinishGetTokenCallback ( key , token . Token ,
190+ token . ExpireTimeMs , 0 , "" ) ;
191+ }
192+ } ) ;
193+ }
194+
195+ [ MonoPInvokeCallback ( typeof ( AppCheckUtil . TokenChangedDelegate ) ) ]
196+ private static void TokenChangedMethod ( string appName , System . IntPtr tokenCPtr ) {
197+ AppCheckTokenInternal tokenInternal = new AppCheckTokenInternal ( tokenCPtr , false ) ;
198+ AppCheckToken token = AppCheckToken . FromAppCheckTokenInternal ( tokenInternal ) ;
199+
200+ FirebaseAppCheck appCheck ;
201+ if ( appCheckMap . TryGetValue ( appName , out appCheck ) ) {
202+ appCheck . OnTokenChanged ( token ) ;
203+ }
204+ }
103205}
104206
105207}
0 commit comments