Skip to content

Commit af953ad

Browse files
committed
Add support for limited use tokens
1 parent d8cb105 commit af953ad

File tree

6 files changed

+72
-14
lines changed

6 files changed

+72
-14
lines changed

firebase-ai/src/main/kotlin/com/google/firebase/ai/FirebaseAI.kt

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ internal constructor(
4646
@Blocking private val blockingDispatcher: CoroutineContext,
4747
private val appCheckProvider: Provider<InteropAppCheckTokenProvider>,
4848
private val internalAuthProvider: Provider<InternalAuthProvider>,
49+
private val useLimitedUseAppCheckTokens: Boolean
4950
) {
5051

5152
/**
@@ -92,6 +93,7 @@ internal constructor(
9293
modelUri,
9394
firebaseApp.options.apiKey,
9495
firebaseApp,
96+
useLimitedUseAppCheckTokens,
9597
generationConfig,
9698
safetySettings,
9799
tools,
@@ -152,7 +154,8 @@ internal constructor(
152154
requestOptions,
153155
appCheckProvider.get(),
154156
internalAuthProvider.get(),
155-
backend
157+
backend,
158+
useLimitedUseAppCheckTokens,
156159
)
157160
}
158161

@@ -194,6 +197,7 @@ internal constructor(
194197
modelUri,
195198
firebaseApp.options.apiKey,
196199
firebaseApp,
200+
useLimitedUseAppCheckTokens,
197201
generationConfig,
198202
safetySettings,
199203
requestOptions,
@@ -212,15 +216,33 @@ internal constructor(
212216
* Returns the [FirebaseAI] instance for the provided [FirebaseApp] and [backend].
213217
*
214218
* @param backend the backend reference to make generative AI requests to.
219+
* @param useLimitedUseAppCheckTokens when sending tokens to the backend, this option enables
220+
* the usage of App Check's limited-use tokens instead of the standard cached tokens.
221+
*
222+
* A new limited-use tokens will be generated for each request; providing a smaller attack
223+
* surface for malicious parties to hijack tokens. When used alongside replay protection,
224+
* limited-use tokens are also _consumed_ after each request, ensuring they can't be used again.
225+
*
226+
* _This flag is set to `false` by default._
227+
*
228+
* > **Important:** Replay protection is not currently supported for the FirebaseAI backend. >
229+
* While this feature is being developed, you can still migrate to using > limited-use tokens.
230+
* Because limited-use tokens are backwards compatible, you can still > use them without replay
231+
* protection. Due to their shorter TTL over standard App Check > tokens, they still provide a
232+
* security benefit. > Migrating to limited-use tokens sooner minimizes disruption when support
233+
* for replay > protection is added.
215234
*/
216235
@JvmStatic
217236
@JvmOverloads
218237
public fun getInstance(
219238
app: FirebaseApp = Firebase.app,
220-
backend: GenerativeBackend
239+
backend: GenerativeBackend,
240+
useLimitedUseAppCheckTokens: Boolean = false,
221241
): FirebaseAI {
222242
val multiResourceComponent = app[FirebaseAIMultiResourceComponent::class.java]
223-
return multiResourceComponent.get(backend)
243+
return multiResourceComponent.get(
244+
InstanceKey(backend.location, backend, useLimitedUseAppCheckTokens)
245+
)
224246
}
225247

226248
/** The [FirebaseAI] instance for the provided [FirebaseApp] using the Google AI Backend. */
@@ -244,8 +266,11 @@ public val Firebase.ai: FirebaseAI
244266
* Returns the [FirebaseAI] instance for the provided [FirebaseApp] and [backend].
245267
*
246268
* @param backend the backend reference to make generative AI requests to.
269+
* @param useLimitedUseAppCheckTokens use App Check's limited-use tokens when sending requests to
270+
* the backend. To learn more about what this means, see the full docs on [FirebaseAI.getInstance].
247271
*/
248272
public fun Firebase.ai(
249273
app: FirebaseApp = Firebase.app,
250-
backend: GenerativeBackend = GenerativeBackend.googleAI()
251-
): FirebaseAI = FirebaseAI.getInstance(app, backend)
274+
backend: GenerativeBackend = GenerativeBackend.googleAI(),
275+
useLimitedUseAppCheckTokens: Boolean = false
276+
): FirebaseAI = FirebaseAI.getInstance(app, backend, useLimitedUseAppCheckTokens)

firebase-ai/src/main/kotlin/com/google/firebase/ai/FirebaseAIMultiResourceComponent.kt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,25 @@ internal class FirebaseAIMultiResourceComponent(
3737
private val internalAuthProvider: Provider<InternalAuthProvider>,
3838
) {
3939

40-
@GuardedBy("this") private val instances: MutableMap<String, FirebaseAI> = mutableMapOf()
40+
@GuardedBy("this") private val instances: MutableMap<InstanceKey, FirebaseAI> = mutableMapOf()
4141

42-
fun get(backend: GenerativeBackend): FirebaseAI =
42+
fun get(key: InstanceKey): FirebaseAI =
4343
synchronized(this) {
44-
instances.getOrPut(backend.location) {
44+
instances.getOrPut(key) {
4545
FirebaseAI(
4646
app,
47-
backend,
47+
key.backend,
4848
blockingDispatcher,
4949
appCheckProvider,
5050
internalAuthProvider,
51+
key.useLimitedUseAppCheckTokens
5152
)
5253
}
5354
}
5455
}
56+
57+
internal data class InstanceKey(
58+
val location: String,
59+
val backend: GenerativeBackend,
60+
val useLimitedUseAppCheckTokens: Boolean
61+
)

firebase-ai/src/main/kotlin/com/google/firebase/ai/GenerativeModel.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ internal constructor(
6565
modelName: String,
6666
apiKey: String,
6767
firebaseApp: FirebaseApp,
68+
useLimitedUseAppCheckTokens: Boolean,
6869
generationConfig: GenerationConfig? = null,
6970
safetySettings: List<SafetySetting>? = null,
7071
tools: List<Tool>? = null,
@@ -73,7 +74,7 @@ internal constructor(
7374
requestOptions: RequestOptions = RequestOptions(),
7475
generativeBackend: GenerativeBackend,
7576
appCheckTokenProvider: InteropAppCheckTokenProvider? = null,
76-
internalAuthProvider: InternalAuthProvider? = null,
77+
internalAuthProvider: InternalAuthProvider? = null
7778
) : this(
7879
modelName,
7980
generationConfig,
@@ -88,7 +89,12 @@ internal constructor(
8889
requestOptions,
8990
"gl-kotlin/${KotlinVersion.CURRENT}-ai fire/${BuildConfig.VERSION_NAME}",
9091
firebaseApp,
91-
AppCheckHeaderProvider(TAG, appCheckTokenProvider, internalAuthProvider),
92+
AppCheckHeaderProvider(
93+
TAG,
94+
useLimitedUseAppCheckTokens,
95+
appCheckTokenProvider,
96+
internalAuthProvider
97+
),
9298
),
9399
)
94100

firebase-ai/src/main/kotlin/com/google/firebase/ai/ImagenModel.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ internal constructor(
5858
modelName: String,
5959
apiKey: String,
6060
firebaseApp: FirebaseApp,
61+
useLimitedUseAppCheckTokens: Boolean,
6162
generationConfig: ImagenGenerationConfig? = null,
6263
safetySettings: ImagenSafetySettings? = null,
6364
requestOptions: RequestOptions = RequestOptions(),
@@ -73,7 +74,12 @@ internal constructor(
7374
requestOptions,
7475
"gl-kotlin/${KotlinVersion.CURRENT}-ai fire/${BuildConfig.VERSION_NAME}",
7576
firebaseApp,
76-
AppCheckHeaderProvider(TAG, appCheckTokenProvider, internalAuthProvider),
77+
AppCheckHeaderProvider(
78+
TAG,
79+
useLimitedUseAppCheckTokens,
80+
appCheckTokenProvider,
81+
internalAuthProvider
82+
),
7783
),
7884
)
7985

firebase-ai/src/main/kotlin/com/google/firebase/ai/LiveGenerativeModel.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ internal constructor(
7070
appCheckTokenProvider: InteropAppCheckTokenProvider? = null,
7171
internalAuthProvider: InternalAuthProvider? = null,
7272
generativeBackend: GenerativeBackend,
73+
useLimitedUseAppCheckTokens: Boolean,
7374
) : this(
7475
modelName,
7576
blockingDispatcher,
@@ -83,7 +84,12 @@ internal constructor(
8384
requestOptions,
8485
"gl-kotlin/${KotlinVersion.CURRENT}-ai fire/${BuildConfig.VERSION_NAME}",
8586
firebaseApp,
86-
AppCheckHeaderProvider(TAG, appCheckTokenProvider, internalAuthProvider),
87+
AppCheckHeaderProvider(
88+
TAG,
89+
useLimitedUseAppCheckTokens,
90+
appCheckTokenProvider,
91+
internalAuthProvider
92+
),
8793
generativeBackend
8894
),
8995
)

firebase-ai/src/main/kotlin/com/google/firebase/ai/common/AppCheckHeaderProvider.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import kotlinx.coroutines.tasks.await
2525

2626
internal class AppCheckHeaderProvider(
2727
private val logTag: String,
28+
private val useLimitedUseAppCheckTokens: Boolean,
2829
private val appCheckTokenProvider: InteropAppCheckTokenProvider? = null,
2930
private val internalAuthProvider: InternalAuthProvider? = null,
3031
) : HeaderProvider {
@@ -36,7 +37,14 @@ internal class AppCheckHeaderProvider(
3637
if (appCheckTokenProvider == null) {
3738
Log.w(logTag, "AppCheck not registered, skipping")
3839
} else {
39-
val token = appCheckTokenProvider.getToken(false).await()
40+
val result =
41+
if (useLimitedUseAppCheckTokens) {
42+
appCheckTokenProvider.limitedUseToken
43+
} else {
44+
appCheckTokenProvider.getToken(false)
45+
}
46+
47+
val token = result.await()
4048

4149
if (token.error != null) {
4250
Log.w(logTag, "Error obtaining AppCheck token", token.error)

0 commit comments

Comments
 (0)