@@ -24,12 +24,21 @@ public sealed class FirebaseAppCheck {
24
24
// The C++ object that this wraps.
25
25
private AppCheckInternal appCheckInternal ;
26
26
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 > ( ) ;
29
31
// The user provided Factory.
30
32
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 ) ;
33
42
34
43
// Make the constructor private, since users aren't meant to make it.
35
44
private FirebaseAppCheck ( AppCheckInternal internalObject ) {
@@ -55,11 +64,10 @@ public static FirebaseAppCheck DefaultInstance {
55
64
/// {@link FirebaseApp} instance.
56
65
public static FirebaseAppCheck GetInstance ( FirebaseApp app ) {
57
66
FirebaseAppCheck result ;
58
- if ( ! appCheckMap . TryGetValue ( app , out result ) ) {
67
+ if ( ! appCheckMap . TryGetValue ( app . Name , out result ) ) {
59
68
AppCheckInternal internalObject = AppCheckInternal . GetInstance ( app ) ;
60
69
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 ;
63
71
}
64
72
return result ;
65
73
}
@@ -79,8 +87,20 @@ public static FirebaseAppCheck GetInstance(FirebaseApp app) {
79
87
///
80
88
/// This method should be called before initializing the Firebase App.
81
89
public static void SetAppCheckProviderFactory ( IAppCheckProviderFactory factory ) {
90
+ if ( appCheckFactory == factory ) return ;
91
+
82
92
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
+ }
84
104
}
85
105
86
106
/// Sets the {@code isTokenAutoRefreshEnabled} flag.
@@ -95,11 +115,93 @@ public void SetTokenAutoRefreshEnabled(bool isTokenAutoRefreshEnabled) {
95
115
public System . Threading . Tasks . Task < AppCheckToken >
96
116
GetAppCheckTokenAsync ( bool forceRefresh ) {
97
117
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
+ } ) ;
99
125
}
100
126
101
127
/// 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
+ }
103
205
}
104
206
105
207
}
0 commit comments