Skip to content

Commit 5844b72

Browse files
Daeda88avdyushin
andauthored
Increase Auth coverage (#79)
* Update FirebaseCore.def * Adding missing Auth calls * Add iOS implementation * Add providers * Trying to resolve Firestore issues * Added iOS methods * Add Javascript * Cleaned up .def files Dependencies now part of actual submodules * Add Test * Fixed compile for tests * Add missing firetore frameworks * Account for nullable results * Use Carthage instead of zip for linking. This cleans up dependencies and reduces build time * Track firestore version * Update yaml * Remove useless podspec * Code cleanup * Fixing remarks * Move ActionData into operator * fix test Co-authored-by: Grigory Avdyushin <[email protected]>
1 parent 7552695 commit 5844b72

File tree

18 files changed

+1463
-138
lines changed

18 files changed

+1463
-138
lines changed

firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/auth.kt

Lines changed: 98 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
* Copyright (c) 2020 GitLive Ltd. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

5+
@file:JvmName("android")
56
package dev.gitlive.firebase.auth
67

7-
import com.google.firebase.auth.EmailAuthProvider
8+
import com.google.firebase.auth.ActionCodeEmailInfo
9+
import com.google.firebase.auth.ActionCodeMultiFactorInfo
810
import com.google.firebase.auth.FirebaseAuth.AuthStateListener
911
import dev.gitlive.firebase.Firebase
1012
import dev.gitlive.firebase.FirebaseApp
1113
import kotlinx.coroutines.channels.awaitClose
14+
import kotlinx.coroutines.flow.Flow
1215
import kotlinx.coroutines.flow.callbackFlow
1316
import kotlinx.coroutines.tasks.await
1417

@@ -22,71 +25,129 @@ actual class FirebaseAuth internal constructor(val android: com.google.firebase.
2225
actual val currentUser: FirebaseUser?
2326
get() = android.currentUser?.let { FirebaseUser(it) }
2427

25-
actual suspend fun sendPasswordResetEmail(email: String) {
26-
android.sendPasswordResetEmail(email).await()
28+
actual val authStateChanged get() = callbackFlow {
29+
val listener = AuthStateListener { auth -> offer(auth.currentUser?.let { FirebaseUser(it) }) }
30+
android.addAuthStateListener(listener)
31+
awaitClose { android.removeAuthStateListener(listener) }
2732
}
2833

29-
actual suspend fun signInWithEmailAndPassword(email: String, password: String) =
30-
AuthResult(android.signInWithEmailAndPassword(email, password).await())
34+
actual val idTokenChanged: Flow<FirebaseUser?>
35+
get() = callbackFlow {
36+
val listener = com.google.firebase.auth.FirebaseAuth.IdTokenListener { auth -> offer(auth.currentUser?.let { FirebaseUser(it) })}
37+
android.addIdTokenListener(listener)
38+
awaitClose { android.removeIdTokenListener(listener) }
39+
}
40+
41+
actual var languageCode: String
42+
get() = android.languageCode ?: ""
43+
set(value) { android.setLanguageCode(value) }
44+
45+
actual suspend fun applyActionCode(code: String) = android.applyActionCode(code).await().run { Unit }
46+
actual suspend fun checkActionCode(code: String): ActionCodeResult = ActionCodeResult(android.checkActionCode(code).await())
47+
actual suspend fun confirmPasswordReset(code: String, newPassword: String) = android.confirmPasswordReset(code, newPassword).await().run { Unit }
3148

3249
actual suspend fun createUserWithEmailAndPassword(email: String, password: String) =
3350
AuthResult(android.createUserWithEmailAndPassword(email, password).await())
3451

52+
actual suspend fun fetchSignInMethodsForEmail(email: String): SignInMethodQueryResult = SignInMethodQueryResult(android.fetchSignInMethodsForEmail(email).await())
53+
54+
actual suspend fun sendPasswordResetEmail(email: String, actionCodeSettings: ActionCodeSettings?) {
55+
android.sendPasswordResetEmail(email, actionCodeSettings?.android).await()
56+
}
57+
58+
actual suspend fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings) = android.sendSignInLinkToEmail(email, actionCodeSettings.android).await().run { Unit }
59+
60+
actual suspend fun signInWithEmailAndPassword(email: String, password: String) =
61+
AuthResult(android.signInWithEmailAndPassword(email, password).await())
62+
3563
actual suspend fun signInWithCustomToken(token: String) =
3664
AuthResult(android.signInWithCustomToken(token).await())
3765

3866
actual suspend fun signInAnonymously() = AuthResult(android.signInAnonymously().await())
3967

40-
actual val authStateChanged get() = callbackFlow {
41-
val listener = object : AuthStateListener {
42-
override fun onAuthStateChanged(auth: com.google.firebase.auth.FirebaseAuth) {
43-
offer(auth.currentUser?.let { FirebaseUser(it) })
44-
}
45-
}
46-
android.addAuthStateListener(listener)
47-
awaitClose { android.removeAuthStateListener(listener) }
48-
}
4968
actual suspend fun signInWithCredential(authCredential: AuthCredential) =
5069
AuthResult(android.signInWithCredential(authCredential.android).await())
5170

5271
actual suspend fun signOut() = android.signOut()
53-
}
5472

55-
actual class AuthCredential(val android: com.google.firebase.auth.AuthCredential)
73+
actual suspend fun updateCurrentUser(user: FirebaseUser) = android.updateCurrentUser(user.android).await().run { Unit }
74+
actual suspend fun verifyPasswordResetCode(code: String): String = android.verifyPasswordResetCode(code).await()
75+
}
5676

5777
actual class AuthResult internal constructor(val android: com.google.firebase.auth.AuthResult) {
5878
actual val user: FirebaseUser?
5979
get() = android.user?.let { FirebaseUser(it) }
6080
}
6181

62-
actual class FirebaseUser internal constructor(val android: com.google.firebase.auth.FirebaseUser) {
63-
actual val uid: String
64-
get() = android.uid
65-
actual val displayName: String?
66-
get() = android.displayName
67-
actual val email: String?
68-
get() = android.email
69-
actual val phoneNumber: String?
70-
get() = android.phoneNumber
71-
actual val isAnonymous: Boolean
72-
get() = android.isAnonymous
73-
actual suspend fun delete() = android.delete().await().run { Unit }
74-
actual suspend fun reload() = android.reload().await().run { Unit }
75-
actual suspend fun sendEmailVerification() = android.sendEmailVerification().await().run { Unit }
82+
actual class ActionCodeResult(val android: com.google.firebase.auth.ActionCodeResult) {
83+
actual val operation: Operation
84+
get() = when (android.operation) {
85+
com.google.firebase.auth.ActionCodeResult.PASSWORD_RESET -> Operation.PasswordReset(this)
86+
com.google.firebase.auth.ActionCodeResult.VERIFY_EMAIL -> Operation.VerifyEmail(this)
87+
com.google.firebase.auth.ActionCodeResult.RECOVER_EMAIL -> Operation.RecoverEmail(this)
88+
com.google.firebase.auth.ActionCodeResult.ERROR -> Operation.Error
89+
com.google.firebase.auth.ActionCodeResult.SIGN_IN_WITH_EMAIL_LINK -> Operation.SignInWithEmailLink
90+
com.google.firebase.auth.ActionCodeResult.VERIFY_BEFORE_CHANGE_EMAIL -> Operation.VerifyBeforeChangeEmail(this)
91+
com.google.firebase.auth.ActionCodeResult.REVERT_SECOND_FACTOR_ADDITION -> Operation.RevertSecondFactorAddition(this)
92+
else -> Operation.Error
93+
}
94+
}
95+
96+
internal actual sealed class ActionCodeDataType<out T> {
97+
98+
actual abstract fun dataForResult(result: ActionCodeResult): T
99+
100+
actual object Email : ActionCodeDataType<String>() {
101+
override fun dataForResult(result: ActionCodeResult): String = result.android.info!!.email
102+
}
103+
actual object PreviousEmail : ActionCodeDataType<String>() {
104+
override fun dataForResult(result: ActionCodeResult): String = (result.android.info as ActionCodeEmailInfo).previousEmail
105+
}
106+
actual object MultiFactor : ActionCodeDataType<MultiFactorInfo?>() {
107+
override fun dataForResult(result: ActionCodeResult): MultiFactorInfo? = (result.android.info as? ActionCodeMultiFactorInfo)?.multiFactorInfo?.let { MultiFactorInfo(it) }
108+
}
109+
}
110+
111+
actual class SignInMethodQueryResult(val android: com.google.firebase.auth.SignInMethodQueryResult) {
112+
actual val signInMethods: List<String>
113+
get() = android.signInMethods ?: emptyList()
114+
}
115+
116+
actual class ActionCodeSettings private constructor(val android: com.google.firebase.auth.ActionCodeSettings) {
117+
118+
actual constructor(url: String,
119+
androidPackageName: AndroidPackageName?,
120+
dynamicLinkDomain: String?,
121+
canHandleCodeInApp: Boolean,
122+
iOSBundleId: String?
123+
) : this(com.google.firebase.auth.ActionCodeSettings.newBuilder().apply {
124+
this.url = url
125+
androidPackageName?.let {
126+
this.setAndroidPackageName(it.androidPackageName, it.installIfNotAvailable, it.minimumVersion)
127+
}
128+
this.dynamicLinkDomain = dynamicLinkDomain
129+
this.handleCodeInApp = canHandleCodeInApp
130+
this.iosBundleId = iosBundleId
131+
}.build())
132+
133+
actual val canHandleCodeInApp: Boolean
134+
get() = android.canHandleCodeInApp()
135+
actual val androidPackageName: AndroidPackageName?
136+
get() = android.androidPackageName?.let {
137+
AndroidPackageName(it, android.androidInstallApp, android.androidMinimumVersion)
138+
}
139+
actual val iOSBundle: String?
140+
get() = android.iosBundle
141+
actual val url: String
142+
get() = android.url
76143
}
77144

78145
actual typealias FirebaseAuthException = com.google.firebase.auth.FirebaseAuthException
79146
actual typealias FirebaseAuthActionCodeException = com.google.firebase.auth.FirebaseAuthActionCodeException
80147
actual typealias FirebaseAuthEmailException = com.google.firebase.auth.FirebaseAuthEmailException
81148
actual typealias FirebaseAuthInvalidCredentialsException = com.google.firebase.auth.FirebaseAuthInvalidCredentialsException
82149
actual typealias FirebaseAuthInvalidUserException = com.google.firebase.auth.FirebaseAuthInvalidUserException
150+
actual typealias FirebaseAuthMultiFactorException = com.google.firebase.auth.FirebaseAuthMultiFactorException
83151
actual typealias FirebaseAuthRecentLoginRequiredException = com.google.firebase.auth.FirebaseAuthRecentLoginRequiredException
84152
actual typealias FirebaseAuthUserCollisionException = com.google.firebase.auth.FirebaseAuthUserCollisionException
85153
actual typealias FirebaseAuthWebException = com.google.firebase.auth.FirebaseAuthWebException
86-
87-
actual object EmailAuthProvider {
88-
actual fun credentialWithEmail(
89-
email: String,
90-
password: String
91-
): AuthCredential = AuthCredential(EmailAuthProvider.getCredential(email, password))
92-
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright (c) 2020 GitLive Ltd. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package dev.gitlive.firebase.auth
6+
7+
import android.app.Activity
8+
import com.google.firebase.FirebaseException
9+
import com.google.firebase.auth.PhoneAuthProvider
10+
import kotlinx.coroutines.CompletableDeferred
11+
import kotlinx.coroutines.coroutineScope
12+
import kotlinx.coroutines.launch
13+
import kotlinx.coroutines.tasks.await
14+
import java.util.concurrent.TimeUnit
15+
16+
actual open class AuthCredential(open val android: com.google.firebase.auth.AuthCredential) {
17+
actual val providerId: String
18+
get() = android.provider
19+
}
20+
21+
actual class PhoneAuthCredential(override val android: com.google.firebase.auth.PhoneAuthCredential) : AuthCredential(android)
22+
23+
actual class OAuthCredential(override val android: com.google.firebase.auth.OAuthCredential) : AuthCredential(android)
24+
25+
actual object EmailAuthProvider {
26+
actual fun credential(
27+
email: String,
28+
password: String
29+
): AuthCredential = AuthCredential(com.google.firebase.auth.EmailAuthProvider.getCredential(email, password))
30+
}
31+
32+
actual object FacebookAuthProvider {
33+
actual fun credential(accessToken: String): AuthCredential = AuthCredential(com.google.firebase.auth.FacebookAuthProvider.getCredential(accessToken))
34+
}
35+
36+
actual object GithubAuthProvider {
37+
actual fun credential(token: String): AuthCredential = AuthCredential(com.google.firebase.auth.GithubAuthProvider.getCredential(token))
38+
}
39+
40+
actual object GoogleAuthProvider {
41+
actual fun credential(idToken: String, accessToken: String): AuthCredential = AuthCredential(com.google.firebase.auth.GoogleAuthProvider.getCredential(idToken, accessToken))
42+
}
43+
44+
actual class OAuthProvider(val android: com.google.firebase.auth.OAuthProvider.Builder, private val auth: FirebaseAuth) {
45+
actual constructor(provider: String, auth: FirebaseAuth) : this(com.google.firebase.auth.OAuthProvider.newBuilder(provider, auth.android), auth)
46+
47+
actual companion object {
48+
actual fun credentials(type: OAuthCredentialsType): AuthCredential {
49+
val credential = com.google.firebase.auth.OAuthProvider.newCredentialBuilder(type.providerId).apply {
50+
when (type) {
51+
is OAuthCredentialsType.AccessToken -> accessToken = type.accessToken
52+
is OAuthCredentialsType.IdAndAccessToken -> {
53+
accessToken = type.accessToken
54+
setIdToken(type.idToken)
55+
}
56+
is OAuthCredentialsType.IdAndAccessTokenAndRawNonce -> {
57+
accessToken = type.accessToken
58+
setIdTokenWithRawNonce(type.idToken, type.rawNonce)
59+
}
60+
is OAuthCredentialsType.IdTokenAndRawNonce -> {
61+
setIdTokenWithRawNonce(type.idToken, type.rawNonce)
62+
}
63+
}
64+
}.build()
65+
return (credential as? com.google.firebase.auth.OAuthCredential)?.let { OAuthCredential(it) } ?: AuthCredential(credential)
66+
}
67+
}
68+
69+
private var customParameters: Map<String, String> = emptyMap()
70+
71+
actual fun addScope(vararg scope: String) {
72+
android.scopes = android.scopes + scope.asList()
73+
}
74+
actual fun setCustomParameters(parameters: Map<String, String>) {
75+
customParameters = parameters
76+
}
77+
78+
actual suspend fun signIn(signInProvider: SignInProvider): AuthResult = AuthResult(auth.android.startActivityForSignInWithProvider(signInProvider, android.apply { addCustomParameters(customParameters) }.build()).await())
79+
}
80+
81+
actual typealias SignInProvider = Activity
82+
83+
actual class PhoneAuthProvider(val android: com.google.firebase.auth.PhoneAuthProvider) {
84+
85+
86+
actual constructor(auth: FirebaseAuth) : this(com.google.firebase.auth.PhoneAuthProvider.getInstance(auth.android))
87+
88+
actual fun credential(verificationId: String, smsCode: String): PhoneAuthCredential = PhoneAuthCredential(com.google.firebase.auth.PhoneAuthProvider.getCredential(verificationId, smsCode))
89+
actual suspend fun verifyPhoneNumber(phoneNumber: String, verificationProvider: PhoneVerificationProvider): AuthCredential = coroutineScope {
90+
val response = CompletableDeferred<Result<AuthCredential>>()
91+
val callback = object :
92+
PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
93+
94+
override fun onCodeSent(verificationId: String, forceResending: PhoneAuthProvider.ForceResendingToken) {
95+
verificationProvider.codeSent { android.verifyPhoneNumber(phoneNumber, verificationProvider.timeout, verificationProvider.unit, verificationProvider.activity, this, forceResending) }
96+
}
97+
98+
override fun onCodeAutoRetrievalTimeOut(verificationId: String) {
99+
launch {
100+
val code = verificationProvider.getVerificationCode()
101+
try {
102+
val credentials =
103+
credential(verificationId, code)
104+
response.complete(Result.success(credentials))
105+
} catch (e: Exception) {
106+
response.complete(Result.failure(e))
107+
}
108+
}
109+
}
110+
111+
override fun onVerificationCompleted(credential: com.google.firebase.auth.PhoneAuthCredential) {
112+
response.complete(Result.success(AuthCredential(credential)))
113+
}
114+
115+
override fun onVerificationFailed(error: FirebaseException) {
116+
response.complete(Result.failure(error))
117+
}
118+
119+
}
120+
android.verifyPhoneNumber(phoneNumber, verificationProvider.timeout, verificationProvider.unit, verificationProvider.activity, callback)
121+
122+
response.await().getOrThrow()
123+
}
124+
}
125+
126+
actual interface PhoneVerificationProvider {
127+
val activity: Activity
128+
val timeout: Long
129+
val unit: TimeUnit
130+
fun codeSent(triggerResend: (Unit) -> Unit)
131+
suspend fun getVerificationCode(): String
132+
}
133+
134+
actual object TwitterAuthProvider {
135+
actual fun credential(token: String, secret: String): AuthCredential = AuthCredential(com.google.firebase.auth.TwitterAuthProvider.getCredential(token, secret))
136+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) 2020 GitLive Ltd. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package dev.gitlive.firebase.auth
6+
7+
import kotlinx.coroutines.tasks.await
8+
9+
actual class MultiFactor(val android: com.google.firebase.auth.MultiFactor) {
10+
actual val enrolledFactors: List<MultiFactorInfo>
11+
get() = android.enrolledFactors.map { MultiFactorInfo(it) }
12+
actual suspend fun enroll(multiFactorAssertion: MultiFactorAssertion, displayName: String?) = android.enroll(multiFactorAssertion.android, displayName).await().run { Unit }
13+
actual suspend fun getSession(): MultiFactorSession = MultiFactorSession(android.session.await())
14+
actual suspend fun unenroll(multiFactorInfo: MultiFactorInfo) = android.unenroll(multiFactorInfo.android).await().run { Unit }
15+
actual suspend fun unenroll(factorUid: String) = android.unenroll(factorUid).await().run { Unit }
16+
}
17+
18+
actual class MultiFactorInfo(val android: com.google.firebase.auth.MultiFactorInfo) {
19+
actual val displayName: String?
20+
get() = android.displayName
21+
actual val enrollmentTime: Double
22+
get() = android.enrollmentTimestamp.toDouble()
23+
actual val factorId: String
24+
get() = android.factorId
25+
actual val uid: String
26+
get() = android.uid
27+
}
28+
29+
actual class MultiFactorAssertion(val android: com.google.firebase.auth.MultiFactorAssertion) {
30+
actual val factorId: String
31+
get() = android.factorId
32+
}
33+
34+
actual class MultiFactorSession(val android: com.google.firebase.auth.MultiFactorSession)
35+
36+
actual class MultiFactorResolver(val android: com.google.firebase.auth.MultiFactorResolver) {
37+
actual val auth: FirebaseAuth = FirebaseAuth(android.firebaseAuth)
38+
actual val hints: List<MultiFactorInfo> = android.hints.map { MultiFactorInfo(it) }
39+
actual val session: MultiFactorSession = MultiFactorSession(android.session)
40+
41+
actual suspend fun resolveSignIn(assertion: MultiFactorAssertion): AuthResult = AuthResult(android.resolveSignIn(assertion.android).await())
42+
}

0 commit comments

Comments
 (0)