Skip to content

Add FirebaseAuth.signInWithEmailLink() API #212

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Oct 4, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ actual class FirebaseAuth internal constructor(val android: com.google.firebase.

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

actual fun isSignInWithEmailLink(link: String) = android.isSignInWithEmailLink(link)

actual suspend fun signInWithEmailAndPassword(email: String, password: String) =
AuthResult(android.signInWithEmailAndPassword(email, password).await())

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

actual suspend fun signInWithEmailLink(email: String, link: String) =
AuthResult(android.signInWithEmailLink(email, link).await())

actual suspend fun signOut() = android.signOut()

actual suspend fun updateCurrentUser(user: FirebaseUser) = android.updateCurrentUser(user.android).await().run { Unit }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ actual object GithubAuthProvider {
}

actual object GoogleAuthProvider {
actual fun credential(idToken: String, accessToken: String): AuthCredential = AuthCredential(com.google.firebase.auth.GoogleAuthProvider.getCredential(idToken, accessToken))
actual fun credential(idToken: String?, accessToken: String?): AuthCredential {
require(idToken != null || accessToken != null) {
"Both parameters are optional but at least one must be present."
}
return AuthCredential(com.google.firebase.auth.GoogleAuthProvider.getCredential(idToken, accessToken))
}
}

actual class OAuthProvider(val android: com.google.firebase.auth.OAuthProvider) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ expect class FirebaseAuth {
suspend fun fetchSignInMethodsForEmail(email: String): List<String>
suspend fun sendPasswordResetEmail(email: String, actionCodeSettings: ActionCodeSettings? = null)
suspend fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings)
fun isSignInWithEmailLink(link: String): Boolean
suspend fun signInWithEmailAndPassword(email: String, password: String): AuthResult
suspend fun signInWithCustomToken(token: String): AuthResult
suspend fun signInAnonymously(): AuthResult
suspend fun signInWithCredential(authCredential: AuthCredential): AuthResult
suspend fun signInWithEmailLink(email: String, link: String): AuthResult
suspend fun signOut()
suspend fun updateCurrentUser(user: FirebaseUser)
suspend fun verifyPasswordResetCode(code: String): String
Expand Down Expand Up @@ -67,7 +69,11 @@ data class ActionCodeSettings(
val iOSBundleId: String? = null
)

data class AndroidPackageName(val packageName: String, val installIfNotAvailable: Boolean, val minimumVersion: String?)
data class AndroidPackageName(
val packageName: String,
val installIfNotAvailable: Boolean = true,
val minimumVersion: String? = null
)

expect open class FirebaseAuthException : FirebaseException
expect class FirebaseAuthActionCodeException : FirebaseAuthException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ expect object GithubAuthProvider {
}

expect object GoogleAuthProvider {
fun credential(idToken: String, accessToken: String): AuthCredential
fun credential(idToken: String?, accessToken: String?): AuthCredential
}

expect class OAuthProvider constructor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ class FirebaseAuthTest {
assertEquals(uid, result.user!!.uid)
}

@Test
fun testIsSignInWithEmailLink() {
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"
val invalidLink = "http://localhost:9099/emulator/action?mode=signIn&lang=en&&apiKey=fake-api-key&continueUrl=https%3A%2F%2Fexample.com%2Fsignin"
assertTrue(Firebase.auth.isSignInWithEmailLink(validLink))
assertFalse(Firebase.auth.isSignInWithEmailLink(invalidLink))
}

private suspend fun getTestUid(email: String, password: String): String {
val uid = Firebase.auth.let {
val user = try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ actual class FirebaseAuth internal constructor(val ios: FIRAuth) {

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

actual fun isSignInWithEmailLink(link: String) = ios.isSignInWithEmailLink(link)

actual suspend fun signInWithEmailAndPassword(email: String, password: String) =
AuthResult(ios.awaitResult { signInWithEmail(email = email, password = password, completion = it) })

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

actual suspend fun signInWithEmailLink(email: String, link: String) =
AuthResult(ios.awaitResult { signInWithEmail(email = email, link = link, completion = it) })

actual suspend fun signOut() = ios.throwError { signOut(it) }.run { Unit }

actual suspend fun updateCurrentUser(user: FirebaseUser) = ios.await { updateCurrentUser(user.ios, it) }.run { Unit }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ actual object GithubAuthProvider {
}

actual object GoogleAuthProvider {
actual fun credential(idToken: String, accessToken: String): AuthCredential = AuthCredential(FIRGoogleAuthProvider.credentialWithIDToken(idToken, accessToken))
actual fun credential(idToken: String?, accessToken: String?): AuthCredential {
requireNotNull(idToken) { "idToken must not be null" }
requireNotNull(accessToken) { "accessToken must not be null" }
return AuthCredential(FIRGoogleAuthProvider.credentialWithIDToken(idToken, accessToken))
}
}

actual class OAuthProvider(val ios: FIROAuthProvider) {
Expand Down Expand Up @@ -80,4 +84,4 @@ actual interface PhoneVerificationProvider {

actual object TwitterAuthProvider {
actual fun credential(token: String, secret: String): AuthCredential = AuthCredential(FIRTwitterAuthProvider.credentialWithToken(token, secret))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ actual class FirebaseAuth internal constructor(val js: firebase.auth.Auth) {
actual suspend fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings) =
rethrow { js.sendSignInLinkToEmail(email, actionCodeSettings.toJson()).await() }

actual fun isSignInWithEmailLink(link: String) = rethrow { js.isSignInWithEmailLink(link) }

actual suspend fun signInWithEmailAndPassword(email: String, password: String) =
rethrow { AuthResult(js.signInWithEmailAndPassword(email, password).await()) }

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

actual suspend fun signInWithEmailLink(email: String, link: String) =
rethrow { AuthResult(js.signInWithEmailLink(email, link).await()) }

actual suspend fun signOut() = rethrow { js.signOut().await() }

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

internal fun ActionCodeSettings.toJson() = json(
"url" to url,
"android" to (androidPackageName?.run { json("installApp" to installIfNotAvailable, "minimumVersion" to minimumVersion, "packageName" to packageName) } ?: undefined),
"dynamicLinkDomain" to (dynamicLinkDomain ?: undefined),
"handleCodeInApp" to canHandleCodeInApp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ actual object GithubAuthProvider {
}

actual object GoogleAuthProvider {
actual fun credential(idToken: String, accessToken: String): AuthCredential =
AuthCredential(firebase.auth.GoogleAuthProvider.credential(idToken, accessToken))
actual fun credential(idToken: String?, accessToken: String?): AuthCredential {
require(idToken != null || accessToken != null) {
"Both parameters are optional but at least one must be present."
}
return AuthCredential(firebase.auth.GoogleAuthProvider.credential(idToken, accessToken))
}
}

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

actual object TwitterAuthProvider {
actual fun credential(token: String, secret: String): AuthCredential = AuthCredential(firebase.auth.TwitterAuthProvider.credential(token, secret))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,14 @@ external object firebase {
fun fetchSignInMethodsForEmail(email: String): Promise<Array<String>>
fun sendPasswordResetEmail(email: String, actionCodeSettings: Any?): Promise<Unit>
fun sendSignInLinkToEmail(email: String, actionCodeSettings: Any?): Promise<Unit>
fun isSignInWithEmailLink(link: String): Boolean
fun signInWithEmailAndPassword(email: String, password: String): Promise<AuthResult>
fun signInWithCustomToken(token: String): Promise<AuthResult>
fun signInAnonymously(): Promise<AuthResult>
fun signInWithCredential(authCredential: AuthCredential): Promise<AuthResult>
fun signInWithPopup(provider: AuthProvider): Promise<AuthResult>
fun signInWithRedirect(provider: AuthProvider): Promise<Unit>
fun signInWithEmailLink(email: String, link: String): Promise<AuthResult>
fun getRedirectResult(): Promise<AuthResult>
fun signOut(): Promise<Unit>
fun updateCurrentUser(user: user.User?): Promise<Unit>
Expand Down