1
+ /*
2
+ * Copyright 2025 Google Inc. All Rights Reserved.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5
+ * in compliance with the License. You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software distributed under the
10
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11
+ * express or implied. See the License for the specific language governing permissions and
12
+ * limitations under the License.
13
+ */
14
+
15
+ package com.firebase.ui.auth.compose
16
+
17
+ import androidx.annotation.RestrictTo
18
+ import com.google.firebase.FirebaseApp
19
+ import com.google.firebase.auth.FirebaseAuth
20
+ import com.google.firebase.auth.ktx.auth
21
+ import com.google.firebase.ktx.Firebase
22
+ import java.util.concurrent.ConcurrentHashMap
23
+
24
+ /* *
25
+ * The central class that coordinates all authentication operations for Firebase Auth UI Compose.
26
+ * This class manages UI state and provides methods for signing in, signing up, and managing
27
+ * user accounts.
28
+ *
29
+ * <h2>Usage</h2>
30
+ *
31
+ * **Default app instance:**
32
+ * ```kotlin
33
+ * val authUI = FirebaseAuthUI.getInstance()
34
+ * ```
35
+ *
36
+ * **Custom app instance:**
37
+ * ```kotlin
38
+ * val customApp = Firebase.app("secondary")
39
+ * val authUI = FirebaseAuthUI.getInstance(customApp)
40
+ * ```
41
+ *
42
+ * **Multi-tenancy with custom auth:**
43
+ * ```kotlin
44
+ * val customAuth = Firebase.auth(customApp).apply {
45
+ * tenantId = "my-tenant-id"
46
+ * }
47
+ * val authUI = FirebaseAuthUI.create(customApp, customAuth)
48
+ * ```
49
+ *
50
+ * @property app The [FirebaseApp] instance used for authentication
51
+ * @property auth The [FirebaseAuth] instance used for authentication operations
52
+ *
53
+ * @since 10.0.0
54
+ */
55
+ class FirebaseAuthUI private constructor(
56
+ val app : FirebaseApp ,
57
+ val auth : FirebaseAuth
58
+ ) {
59
+ companion object {
60
+ /* * Cache for singleton instances per FirebaseApp. Thread-safe via ConcurrentHashMap. */
61
+ private val instanceCache = ConcurrentHashMap <String , FirebaseAuthUI >()
62
+
63
+ /* * Special key for the default app instance to distinguish from named instances. */
64
+ private const val DEFAULT_APP_KEY = " __FIREBASE_UI_DEFAULT__"
65
+
66
+ /* *
67
+ * Returns a cached singleton instance for the default Firebase app.
68
+ *
69
+ * This method ensures that the same instance is returned for the default app across the
70
+ * entire application lifecycle. The instance is lazily created on first access and cached
71
+ * for subsequent calls.
72
+ *
73
+ * **Example:**
74
+ * ```kotlin
75
+ * val authUI = FirebaseAuthUI.getInstance()
76
+ * val user = authUI.auth.currentUser
77
+ * ```
78
+ *
79
+ * @return The cached [FirebaseAuthUI] instance for the default app
80
+ * @throws IllegalStateException if Firebase has not been initialized. Call
81
+ * `FirebaseApp.initializeApp(Context)` before using this method.
82
+ */
83
+ @JvmStatic
84
+ fun getInstance (): FirebaseAuthUI {
85
+ val defaultApp = try {
86
+ FirebaseApp .getInstance()
87
+ } catch (e: IllegalStateException ) {
88
+ throw IllegalStateException (
89
+ " Default FirebaseApp is not initialized. " +
90
+ " Make sure to call FirebaseApp.initializeApp(Context) first." ,
91
+ e
92
+ )
93
+ }
94
+
95
+ return instanceCache.getOrPut(DEFAULT_APP_KEY ) {
96
+ FirebaseAuthUI (defaultApp, Firebase .auth)
97
+ }
98
+ }
99
+
100
+ /* *
101
+ * Returns a cached instance for a specific Firebase app.
102
+ *
103
+ * Each [FirebaseApp] gets its own distinct instance that is cached for subsequent calls
104
+ * with the same app. This allows for multiple Firebase projects to be used within the
105
+ * same application.
106
+ *
107
+ * **Example:**
108
+ * ```kotlin
109
+ * val secondaryApp = Firebase.app("secondary")
110
+ * val authUI = FirebaseAuthUI.getInstance(secondaryApp)
111
+ * ```
112
+ *
113
+ * @param app The [FirebaseApp] instance to use
114
+ * @return The cached [FirebaseAuthUI] instance for the specified app
115
+ */
116
+ @JvmStatic
117
+ fun getInstance (app : FirebaseApp ): FirebaseAuthUI {
118
+ val cacheKey = app.name
119
+ return instanceCache.getOrPut(cacheKey) {
120
+ FirebaseAuthUI (app, Firebase .auth(app))
121
+ }
122
+ }
123
+
124
+ /* *
125
+ * Creates a new instance with custom configuration, useful for multi-tenancy.
126
+ *
127
+ * This method always returns a new instance and does **not** use caching, allowing for
128
+ * custom [FirebaseAuth] configurations such as tenant IDs or custom authentication states.
129
+ * Use this when you need fine-grained control over the authentication instance.
130
+ *
131
+ * **Example - Multi-tenancy:**
132
+ * ```kotlin
133
+ * val app = Firebase.app("tenant-app")
134
+ * val auth = Firebase.auth(app).apply {
135
+ * tenantId = "customer-tenant-123"
136
+ * }
137
+ * val authUI = FirebaseAuthUI.create(app, auth)
138
+ * ```
139
+ *
140
+ * @param app The [FirebaseApp] instance to use
141
+ * @param auth The [FirebaseAuth] instance with custom configuration
142
+ * @return A new [FirebaseAuthUI] instance with the provided dependencies
143
+ */
144
+ @JvmStatic
145
+ fun create (app : FirebaseApp , auth : FirebaseAuth ): FirebaseAuthUI {
146
+ return FirebaseAuthUI (app, auth)
147
+ }
148
+
149
+ /* *
150
+ * Clears all cached instances. This method is intended for testing purposes only.
151
+ *
152
+ * @suppress This is an internal API and should not be used in production code.
153
+ * @RestrictTo RestrictTo.Scope.TESTS
154
+ */
155
+ @JvmStatic
156
+ @RestrictTo(RestrictTo .Scope .TESTS )
157
+ internal fun clearInstanceCache () {
158
+ instanceCache.clear()
159
+ }
160
+
161
+ /* *
162
+ * Returns the current number of cached instances. This method is intended for testing
163
+ * purposes only.
164
+ *
165
+ * @return The number of cached [FirebaseAuthUI] instances
166
+ * @suppress This is an internal API and should not be used in production code.
167
+ * @RestrictTo RestrictTo.Scope.TESTS
168
+ */
169
+ @JvmStatic
170
+ @RestrictTo(RestrictTo .Scope .TESTS )
171
+ internal fun getCacheSize (): Int {
172
+ return instanceCache.size
173
+ }
174
+ }
175
+ }
0 commit comments