Skip to content

Commit ed436cf

Browse files
authored
feat(AuthUIConfiguration): implement configuration model, DSL builder and tests (#2216)
1 parent 91b0a49 commit ed436cf

File tree

10 files changed

+1184
-4
lines changed

10 files changed

+1184
-4
lines changed

auth/build.gradle.kts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ plugins {
44
id("com.android.library")
55
id("com.vanniktech.maven.publish")
66
id("org.jetbrains.kotlin.android")
7+
alias(libs.plugins.compose.compiler)
78
}
89

910
android {
@@ -67,9 +68,20 @@ android {
6768
kotlinOptions {
6869
jvmTarget = "17"
6970
}
71+
buildFeatures {
72+
compose = true
73+
}
7074
}
7175

7276
dependencies {
77+
implementation(platform(Config.Libs.Androidx.Compose.bom))
78+
implementation(Config.Libs.Androidx.Compose.ui)
79+
implementation(Config.Libs.Androidx.Compose.uiGraphics)
80+
implementation(Config.Libs.Androidx.Compose.material3)
81+
implementation(Config.Libs.Androidx.Compose.foundation)
82+
implementation(Config.Libs.Androidx.Compose.tooling)
83+
implementation(Config.Libs.Androidx.Compose.toolingPreview)
84+
implementation(Config.Libs.Androidx.Compose.activityCompose)
7385
implementation(Config.Libs.Androidx.materialDesign)
7486
implementation(Config.Libs.Androidx.activity)
7587
// The new activity result APIs force us to include Fragment 1.3.0
@@ -101,6 +113,7 @@ dependencies {
101113
testImplementation(Config.Libs.Test.mockito)
102114
testImplementation(Config.Libs.Test.core)
103115
testImplementation(Config.Libs.Test.robolectric)
116+
testImplementation(Config.Libs.Test.kotlinReflect)
104117
testImplementation(Config.Libs.Provider.facebook)
105118

106119
debugImplementation(project(":internal:lintchecks"))
Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
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.configuration
16+
17+
import android.graphics.Color
18+
import androidx.compose.ui.graphics.vector.ImageVector
19+
import com.google.firebase.auth.ActionCodeSettings
20+
import com.google.firebase.auth.EmailAuthProvider
21+
import com.google.firebase.auth.FacebookAuthProvider
22+
import com.google.firebase.auth.GithubAuthProvider
23+
import com.google.firebase.auth.GoogleAuthProvider
24+
import com.google.firebase.auth.PhoneAuthProvider
25+
import com.google.firebase.auth.TwitterAuthProvider
26+
27+
@AuthUIConfigurationDsl
28+
class AuthProvidersBuilder {
29+
private val providers = mutableListOf<AuthProvider>()
30+
31+
fun provider(provider: AuthProvider) {
32+
providers.add(provider)
33+
}
34+
35+
internal fun build(): List<AuthProvider> = providers.toList()
36+
}
37+
38+
/**
39+
* Enum class to represent all possible providers.
40+
*/
41+
internal enum class Provider(val id: String) {
42+
GOOGLE(GoogleAuthProvider.PROVIDER_ID),
43+
FACEBOOK(FacebookAuthProvider.PROVIDER_ID),
44+
TWITTER(TwitterAuthProvider.PROVIDER_ID),
45+
GITHUB(GithubAuthProvider.PROVIDER_ID),
46+
EMAIL(EmailAuthProvider.PROVIDER_ID),
47+
PHONE(PhoneAuthProvider.PROVIDER_ID),
48+
ANONYMOUS("anonymous"),
49+
MICROSOFT("microsoft.com"),
50+
YAHOO("yahoo.com"),
51+
APPLE("apple.com"),
52+
}
53+
54+
/**
55+
* Base abstract class for OAuth authentication providers with common properties.
56+
*/
57+
abstract class OAuthProvider(
58+
override val providerId: String,
59+
open val scopes: List<String> = emptyList(),
60+
open val customParameters: Map<String, String> = emptyMap()
61+
) : AuthProvider(providerId)
62+
63+
/**
64+
* Base abstract class for authentication providers.
65+
*/
66+
abstract class AuthProvider(open val providerId: String) {
67+
/**
68+
* Email/Password authentication provider configuration.
69+
*/
70+
class Email(
71+
/**
72+
* Requires the user to provide a display name. Defaults to true.
73+
*/
74+
val isDisplayNameRequired: Boolean = true,
75+
76+
/**
77+
* Enables email link sign-in, Defaults to false.
78+
*/
79+
val isEmailLinkSignInEnabled: Boolean = false,
80+
81+
/**
82+
* Settings for email link actions.
83+
*/
84+
val actionCodeSettings: ActionCodeSettings?,
85+
86+
/**
87+
* Allows new accounts to be created. Defaults to true.
88+
*/
89+
val isNewAccountsAllowed: Boolean = true,
90+
91+
/**
92+
* The minimum length for a password. Defaults to 6.
93+
*/
94+
val minimumPasswordLength: Int = 6,
95+
96+
/**
97+
* A list of custom password validation rules.
98+
*/
99+
val passwordValidationRules: List<PasswordRule>
100+
) : AuthProvider(providerId = Provider.EMAIL.id) {
101+
fun validate() {
102+
if (isEmailLinkSignInEnabled) {
103+
val actionCodeSettings = actionCodeSettings
104+
?: requireNotNull(actionCodeSettings) {
105+
"ActionCodeSettings cannot be null when using " +
106+
"email link sign in."
107+
}
108+
109+
check(actionCodeSettings.canHandleCodeInApp()) {
110+
"You must set canHandleCodeInApp in your " +
111+
"ActionCodeSettings to true for Email-Link Sign-in."
112+
}
113+
}
114+
}
115+
}
116+
117+
/**
118+
* Phone number authentication provider configuration.
119+
*/
120+
class Phone(
121+
/**
122+
* The default country code to pre-select.
123+
*/
124+
val defaultCountryCode: String?,
125+
126+
/**
127+
* A list of allowed country codes.
128+
*/
129+
val allowedCountries: List<String>?,
130+
131+
/**
132+
* The expected length of the SMS verification code. Defaults to 6.
133+
*/
134+
val smsCodeLength: Int = 6,
135+
136+
/**
137+
* The timeout in seconds for receiving the SMS. Defaults to 60L.
138+
*/
139+
val timeout: Long = 60L,
140+
141+
/**
142+
* Enables instant verification of the phone number. Defaults to true.
143+
*/
144+
val isInstantVerificationEnabled: Boolean = true,
145+
146+
/**
147+
* Enables automatic retrieval of the SMS code. Defaults to true.
148+
*/
149+
val isAutoRetrievalEnabled: Boolean = true
150+
) : AuthProvider(providerId = Provider.PHONE.id)
151+
152+
/**
153+
* Google Sign-In provider configuration.
154+
*/
155+
class Google(
156+
/**
157+
* The list of scopes to request.
158+
*/
159+
override val scopes: List<String>,
160+
161+
/**
162+
* The OAuth 2.0 client ID for your server.
163+
*/
164+
val serverClientId: String?,
165+
166+
/**
167+
* Requests an ID token. Default to true.
168+
*/
169+
val requestIdToken: Boolean = true,
170+
171+
/**
172+
* Requests the user's profile information. Defaults to true.
173+
*/
174+
val requestProfile: Boolean = true,
175+
176+
/**
177+
* Requests the user's email address. Defaults to true.
178+
*/
179+
val requestEmail: Boolean = true,
180+
181+
/**
182+
* A map of custom OAuth parameters.
183+
*/
184+
override val customParameters: Map<String, String> = emptyMap()
185+
) : OAuthProvider(
186+
providerId = Provider.GOOGLE.id,
187+
scopes = scopes,
188+
customParameters = customParameters
189+
)
190+
191+
/**
192+
* Facebook Login provider configuration.
193+
*/
194+
class Facebook(
195+
/**
196+
* The list of scopes (permissions) to request. Defaults to email and public_profile.
197+
*/
198+
override val scopes: List<String> = listOf("email", "public_profile"),
199+
200+
/**
201+
* if true, enable limited login mode. Defaults to false.
202+
*/
203+
val limitedLogin: Boolean = false,
204+
205+
/**
206+
* A map of custom OAuth parameters.
207+
*/
208+
override val customParameters: Map<String, String> = emptyMap()
209+
) : OAuthProvider(
210+
providerId = Provider.FACEBOOK.id,
211+
scopes = scopes,
212+
customParameters = customParameters
213+
)
214+
215+
/**
216+
* Twitter/X authentication provider configuration.
217+
*/
218+
class Twitter(
219+
/**
220+
* A map of custom OAuth parameters.
221+
*/
222+
override val customParameters: Map<String, String>
223+
) : OAuthProvider(
224+
providerId = Provider.TWITTER.id,
225+
customParameters = customParameters
226+
)
227+
228+
/**
229+
* Github authentication provider configuration.
230+
*/
231+
class Github(
232+
/**
233+
* The list of scopes to request. Defaults to user:email.
234+
*/
235+
override val scopes: List<String> = listOf("user:email"),
236+
237+
/**
238+
* A map of custom OAuth parameters.
239+
*/
240+
override val customParameters: Map<String, String>
241+
) : OAuthProvider(
242+
providerId = Provider.GITHUB.id,
243+
scopes = scopes,
244+
customParameters = customParameters
245+
)
246+
247+
/**
248+
* Microsoft authentication provider configuration.
249+
*/
250+
class Microsoft(
251+
/**
252+
* The list of scopes to request. Defaults to openid, profile, email.
253+
*/
254+
override val scopes: List<String> = listOf("openid", "profile", "email"),
255+
256+
/**
257+
* The tenant ID for Azure Active Directory.
258+
*/
259+
val tenant: String?,
260+
261+
/**
262+
* A map of custom OAuth parameters.
263+
*/
264+
override val customParameters: Map<String, String>
265+
) : OAuthProvider(
266+
providerId = Provider.MICROSOFT.id,
267+
scopes = scopes,
268+
customParameters = customParameters
269+
)
270+
271+
/**
272+
* Yahoo authentication provider configuration.
273+
*/
274+
class Yahoo(
275+
/**
276+
* The list of scopes to request. Defaults to openid, profile, email.
277+
*/
278+
override val scopes: List<String> = listOf("openid", "profile", "email"),
279+
280+
/**
281+
* A map of custom OAuth parameters.
282+
*/
283+
override val customParameters: Map<String, String>
284+
) : OAuthProvider(
285+
providerId = Provider.YAHOO.id,
286+
scopes = scopes,
287+
customParameters = customParameters
288+
)
289+
290+
/**
291+
* Apple Sign-In provider configuration.
292+
*/
293+
class Apple(
294+
/**
295+
* The list of scopes to request. Defaults to name and email.
296+
*/
297+
override val scopes: List<String> = listOf("name", "email"),
298+
299+
/**
300+
* The locale for the sign-in page.
301+
*/
302+
val locale: String?,
303+
304+
/**
305+
* A map of custom OAuth parameters.
306+
*/
307+
override val customParameters: Map<String, String>
308+
) : OAuthProvider(
309+
providerId = Provider.APPLE.id,
310+
scopes = scopes,
311+
customParameters = customParameters
312+
)
313+
314+
/**
315+
* Anonymous authentication provider. It has no configurable properties.
316+
*/
317+
object Anonymous : AuthProvider(providerId = Provider.ANONYMOUS.id)
318+
319+
/**
320+
* A generic OAuth provider for any unsupported provider.
321+
*/
322+
class GenericOAuth(
323+
/**
324+
* The provider ID as configured in the Firebase console.
325+
*/
326+
override val providerId: String,
327+
328+
/**
329+
* The list of scopes to request.
330+
*/
331+
override val scopes: List<String>,
332+
333+
/**
334+
* A map of custom OAuth parameters.
335+
*/
336+
override val customParameters: Map<String, String>,
337+
338+
/**
339+
* The text to display on the provider button.
340+
*/
341+
val buttonLabel: String,
342+
343+
/**
344+
* An optional icon for the provider button.
345+
*/
346+
val buttonIcon: ImageVector?,
347+
348+
/**
349+
* An optional background color for the provider button.
350+
*/
351+
val buttonColor: Color?
352+
) : OAuthProvider(
353+
providerId = providerId,
354+
scopes = scopes,
355+
customParameters = customParameters
356+
)
357+
}

0 commit comments

Comments
 (0)