Skip to content

Commit 6ac7a83

Browse files
authored
Add FirebaseAuth.signInWithEmailLink() API (#212)
* Implement siginInByEmailLink * Add JS ktor engine * Fix Firebase Auth tests * Upgrade serialization plugin * Cleanup * Remove Ktor * Make idToken and accessToken nullable * Add args assertion
1 parent 93df75c commit 6ac7a83

File tree

10 files changed

+53
-8
lines changed

10 files changed

+53
-8
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ actual class FirebaseAuth internal constructor(val android: com.google.firebase.
6666

6767
actual suspend fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings) = android.sendSignInLinkToEmail(email, actionCodeSettings.toAndroid()).await().run { Unit }
6868

69+
actual fun isSignInWithEmailLink(link: String) = android.isSignInWithEmailLink(link)
70+
6971
actual suspend fun signInWithEmailAndPassword(email: String, password: String) =
7072
AuthResult(android.signInWithEmailAndPassword(email, password).await())
7173

@@ -77,6 +79,9 @@ actual class FirebaseAuth internal constructor(val android: com.google.firebase.
7779
actual suspend fun signInWithCredential(authCredential: AuthCredential) =
7880
AuthResult(android.signInWithCredential(authCredential.android).await())
7981

82+
actual suspend fun signInWithEmailLink(email: String, link: String) =
83+
AuthResult(android.signInWithEmailLink(email, link).await())
84+
8085
actual suspend fun signOut() = android.signOut()
8186

8287
actual suspend fun updateCurrentUser(user: FirebaseUser) = android.updateCurrentUser(user.android).await().run { Unit }

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ actual object GithubAuthProvider {
3939
}
4040

4141
actual object GoogleAuthProvider {
42-
actual fun credential(idToken: String, accessToken: String): AuthCredential = AuthCredential(com.google.firebase.auth.GoogleAuthProvider.getCredential(idToken, accessToken))
42+
actual fun credential(idToken: String?, accessToken: String?): AuthCredential {
43+
require(idToken != null || accessToken != null) {
44+
"Both parameters are optional but at least one must be present."
45+
}
46+
return AuthCredential(com.google.firebase.auth.GoogleAuthProvider.getCredential(idToken, accessToken))
47+
}
4348
}
4449

4550
actual class OAuthProvider(val android: com.google.firebase.auth.OAuthProvider) {

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ expect class FirebaseAuth {
2727
suspend fun fetchSignInMethodsForEmail(email: String): List<String>
2828
suspend fun sendPasswordResetEmail(email: String, actionCodeSettings: ActionCodeSettings? = null)
2929
suspend fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings)
30+
fun isSignInWithEmailLink(link: String): Boolean
3031
suspend fun signInWithEmailAndPassword(email: String, password: String): AuthResult
3132
suspend fun signInWithCustomToken(token: String): AuthResult
3233
suspend fun signInAnonymously(): AuthResult
3334
suspend fun signInWithCredential(authCredential: AuthCredential): AuthResult
35+
suspend fun signInWithEmailLink(email: String, link: String): AuthResult
3436
suspend fun signOut()
3537
suspend fun updateCurrentUser(user: FirebaseUser)
3638
suspend fun verifyPasswordResetCode(code: String): String
@@ -67,7 +69,11 @@ data class ActionCodeSettings(
6769
val iOSBundleId: String? = null
6870
)
6971

70-
data class AndroidPackageName(val packageName: String, val installIfNotAvailable: Boolean, val minimumVersion: String?)
72+
data class AndroidPackageName(
73+
val packageName: String,
74+
val installIfNotAvailable: Boolean = true,
75+
val minimumVersion: String? = null
76+
)
7177

7278
expect open class FirebaseAuthException : FirebaseException
7379
expect class FirebaseAuthActionCodeException : FirebaseAuthException

firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/credentials.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ expect object GithubAuthProvider {
2626
}
2727

2828
expect object GoogleAuthProvider {
29-
fun credential(idToken: String, accessToken: String): AuthCredential
29+
fun credential(idToken: String?, accessToken: String?): AuthCredential
3030
}
3131

3232
expect class OAuthProvider constructor(

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@ class FirebaseAuthTest {
103103
assertEquals(uid, result.user!!.uid)
104104
}
105105

106+
@Test
107+
fun testIsSignInWithEmailLink() {
108+
val validLink = "http://localhost:9099/emulator/action?mode=signIn&lang=en&oobCode=_vr0QcFcxcVeLZbrcU-GpTaZiuxlHquqdC8MSy0YM_vzWCTAQgV9Jq&apiKey=fake-api-key&continueUrl=https%3A%2F%2Fexample.com%2Fsignin"
109+
val invalidLink = "http://localhost:9099/emulator/action?mode=signIn&lang=en&&apiKey=fake-api-key&continueUrl=https%3A%2F%2Fexample.com%2Fsignin"
110+
assertTrue(Firebase.auth.isSignInWithEmailLink(validLink))
111+
assertFalse(Firebase.auth.isSignInWithEmailLink(invalidLink))
112+
}
113+
106114
private suspend fun getTestUid(email: String, password: String): String {
107115
val uid = Firebase.auth.let {
108116
val user = try {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ actual class FirebaseAuth internal constructor(val ios: FIRAuth) {
5858

5959
actual suspend fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings) = ios.await { sendSignInLinkToEmail(email, actionCodeSettings.toIos(), it) }.run { Unit }
6060

61+
actual fun isSignInWithEmailLink(link: String) = ios.isSignInWithEmailLink(link)
62+
6163
actual suspend fun signInWithEmailAndPassword(email: String, password: String) =
6264
AuthResult(ios.awaitResult { signInWithEmail(email = email, password = password, completion = it) })
6365

@@ -70,6 +72,9 @@ actual class FirebaseAuth internal constructor(val ios: FIRAuth) {
7072
actual suspend fun signInWithCredential(authCredential: AuthCredential) =
7173
AuthResult(ios.awaitResult { signInWithCredential(authCredential.ios, it) })
7274

75+
actual suspend fun signInWithEmailLink(email: String, link: String) =
76+
AuthResult(ios.awaitResult { signInWithEmail(email = email, link = link, completion = it) })
77+
7378
actual suspend fun signOut() = ios.throwError { signOut(it) }.run { Unit }
7479

7580
actual suspend fun updateCurrentUser(user: FirebaseUser) = ios.await { updateCurrentUser(user.ios, it) }.run { Unit }

firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/credentials.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ actual object GithubAuthProvider {
3131
}
3232

3333
actual object GoogleAuthProvider {
34-
actual fun credential(idToken: String, accessToken: String): AuthCredential = AuthCredential(FIRGoogleAuthProvider.credentialWithIDToken(idToken, accessToken))
34+
actual fun credential(idToken: String?, accessToken: String?): AuthCredential {
35+
requireNotNull(idToken) { "idToken must not be null" }
36+
requireNotNull(accessToken) { "accessToken must not be null" }
37+
return AuthCredential(FIRGoogleAuthProvider.credentialWithIDToken(idToken, accessToken))
38+
}
3539
}
3640

3741
actual class OAuthProvider(val ios: FIROAuthProvider) {
@@ -80,4 +84,4 @@ actual interface PhoneVerificationProvider {
8084

8185
actual object TwitterAuthProvider {
8286
actual fun credential(token: String, secret: String): AuthCredential = AuthCredential(FIRTwitterAuthProvider.credentialWithToken(token, secret))
83-
}
87+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ actual class FirebaseAuth internal constructor(val js: firebase.auth.Auth) {
5353
actual suspend fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings) =
5454
rethrow { js.sendSignInLinkToEmail(email, actionCodeSettings.toJson()).await() }
5555

56+
actual fun isSignInWithEmailLink(link: String) = rethrow { js.isSignInWithEmailLink(link) }
57+
5658
actual suspend fun signInWithEmailAndPassword(email: String, password: String) =
5759
rethrow { AuthResult(js.signInWithEmailAndPassword(email, password).await()) }
5860

@@ -65,6 +67,9 @@ actual class FirebaseAuth internal constructor(val js: firebase.auth.Auth) {
6567
actual suspend fun signInWithCredential(authCredential: AuthCredential) =
6668
rethrow { AuthResult(js.signInWithCredential(authCredential.js).await()) }
6769

70+
actual suspend fun signInWithEmailLink(email: String, link: String) =
71+
rethrow { AuthResult(js.signInWithEmailLink(email, link).await()) }
72+
6873
actual suspend fun signOut() = rethrow { js.signOut().await() }
6974

7075
actual suspend fun updateCurrentUser(user: FirebaseUser) =
@@ -119,6 +124,7 @@ actual class AuthTokenResult(val js: firebase.auth.IdTokenResult) {
119124
}
120125

121126
internal fun ActionCodeSettings.toJson() = json(
127+
"url" to url,
122128
"android" to (androidPackageName?.run { json("installApp" to installIfNotAvailable, "minimumVersion" to minimumVersion, "packageName" to packageName) } ?: undefined),
123129
"dynamicLinkDomain" to (dynamicLinkDomain ?: undefined),
124130
"handleCodeInApp" to canHandleCodeInApp,

firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/credentials.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@ actual object GithubAuthProvider {
2929
}
3030

3131
actual object GoogleAuthProvider {
32-
actual fun credential(idToken: String, accessToken: String): AuthCredential =
33-
AuthCredential(firebase.auth.GoogleAuthProvider.credential(idToken, accessToken))
32+
actual fun credential(idToken: String?, accessToken: String?): AuthCredential {
33+
require(idToken != null || accessToken != null) {
34+
"Both parameters are optional but at least one must be present."
35+
}
36+
return AuthCredential(firebase.auth.GoogleAuthProvider.credential(idToken, accessToken))
37+
}
3438
}
3539

3640
actual class OAuthProvider(val js: firebase.auth.OAuthProvider) {
@@ -81,4 +85,4 @@ actual interface PhoneVerificationProvider {
8185

8286
actual object TwitterAuthProvider {
8387
actual fun credential(token: String, secret: String): AuthCredential = AuthCredential(firebase.auth.TwitterAuthProvider.credential(token, secret))
84-
}
88+
}

firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,14 @@ external object firebase {
5656
fun fetchSignInMethodsForEmail(email: String): Promise<Array<String>>
5757
fun sendPasswordResetEmail(email: String, actionCodeSettings: Any?): Promise<Unit>
5858
fun sendSignInLinkToEmail(email: String, actionCodeSettings: Any?): Promise<Unit>
59+
fun isSignInWithEmailLink(link: String): Boolean
5960
fun signInWithEmailAndPassword(email: String, password: String): Promise<AuthResult>
6061
fun signInWithCustomToken(token: String): Promise<AuthResult>
6162
fun signInAnonymously(): Promise<AuthResult>
6263
fun signInWithCredential(authCredential: AuthCredential): Promise<AuthResult>
6364
fun signInWithPopup(provider: AuthProvider): Promise<AuthResult>
6465
fun signInWithRedirect(provider: AuthProvider): Promise<Unit>
66+
fun signInWithEmailLink(email: String, link: String): Promise<AuthResult>
6567
fun getRedirectResult(): Promise<AuthResult>
6668
fun signOut(): Promise<Unit>
6769
fun updateCurrentUser(user: user.User?): Promise<Unit>

0 commit comments

Comments
 (0)