From f63e1b392a612cbcd1c04b4434e2644e674c744f Mon Sep 17 00:00:00 2001
From: Jakub Kostka <jacob.cube.mexx@gmail.com>
Date: Tue, 10 Sep 2024 14:40:38 +0200
Subject: [PATCH 01/23] signupNewUser implementation

---
 .../com/google/firebase/auth/FirebaseAuth.kt  | 33 ++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index 3a41a23..c0dd4d2 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -245,6 +245,38 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
         return source.task
     }
 
+    fun createUserWithEmailAndPassword(email: String, password: String): Task<AuthResult> {
+        val source = TaskCompletionSource<AuthResult>()
+        val body = RequestBody.create(
+            json,
+            JsonObject(mapOf("email" to JsonPrimitive(email), "password" to JsonPrimitive(password), "returnSecureToken" to JsonPrimitive(true))).toString()
+        )
+        val request = Request.Builder()
+            .url("https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=" + app.options.apiKey)
+            .post(body)
+            .build()
+        client.newCall(request).enqueue(object : Callback {
+
+            override fun onFailure(call: Call, e: IOException) {
+                source.setException(FirebaseException(e.toString(), e))
+            }
+
+            @Throws(IOException::class)
+            override fun onResponse(call: Call, response: Response) {
+                if (!response.isSuccessful) {
+                    source.setException(
+                        createAuthInvalidUserException("signupNewUser", request, response)
+                    )
+                } else {
+                    val responseBody = response.body()?.use { it.string() } ?: ""
+                    val user = FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
+                    refreshToken(user, source) { AuthResult { it } }
+                }
+            }
+        })
+        return source.task
+    }
+
     fun signInWithEmailAndPassword(email: String, password: String): Task<AuthResult> {
         val source = TaskCompletionSource<AuthResult>()
         val body = RequestBody.create(
@@ -425,7 +457,6 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
     }
 
     fun sendPasswordResetEmail(email: String, settings: ActionCodeSettings?): Task<Unit> = TODO()
-    fun createUserWithEmailAndPassword(email: String, password: String): Task<AuthResult> = TODO()
     fun signInWithCredential(authCredential: AuthCredential): Task<AuthResult> = TODO()
     fun checkActionCode(code: String): Task<ActionCodeResult> = TODO()
     fun confirmPasswordReset(code: String, newPassword: String): Task<Unit> = TODO()

From 0972225904fc1d5873a77f0303e49ac05f037204 Mon Sep 17 00:00:00 2001
From: Jakub Kostka <jacob.cube.mexx@gmail.com>
Date: Fri, 13 Sep 2024 19:23:17 +0200
Subject: [PATCH 02/23] email property partially implemented - not all EPs
 return it simple tests added for createUserWithEmailAndPassword,
 signInWithEmailAndPassword, and signInAnonymously.

---
 .../com/google/firebase/auth/FirebaseAuth.kt  | 27 +++++---
 .../com/google/firebase/auth/FirebaseUser.kt  |  2 +-
 src/test/kotlin/FirebaseAuthTest.kt           | 68 +++++++++++++++++++
 3 files changed, 86 insertions(+), 11 deletions(-)
 create mode 100644 src/test/kotlin/FirebaseAuthTest.kt

diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index c0dd4d2..95bd2e7 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -52,17 +52,24 @@ class FirebaseUserImpl private constructor(
     val idToken: String,
     val refreshToken: String,
     val expiresIn: Int,
-    val createdAt: Long
+    val createdAt: Long,
+    override val email: String
 ) : FirebaseUser() {
 
-    constructor(app: FirebaseApp, data: JsonObject, isAnonymous: Boolean = data["isAnonymous"]?.jsonPrimitive?.booleanOrNull ?: false) : this(
-        app,
-        isAnonymous,
-        data["uid"]?.jsonPrimitive?.contentOrNull ?: data["user_id"]?.jsonPrimitive?.contentOrNull ?: data["localId"]?.jsonPrimitive?.contentOrNull ?: "",
-        data["idToken"]?.jsonPrimitive?.contentOrNull ?: data.getValue("id_token").jsonPrimitive.content,
-        data["refreshToken"]?.jsonPrimitive?.contentOrNull ?: data.getValue("refresh_token").jsonPrimitive.content,
-        data["expiresIn"]?.jsonPrimitive?.intOrNull ?: data.getValue("expires_in").jsonPrimitive.int,
-        data["createdAt"]?.jsonPrimitive?.longOrNull ?: System.currentTimeMillis()
+    constructor(
+        app: FirebaseApp,
+        data: JsonObject,
+        isAnonymous: Boolean = data["isAnonymous"]?.jsonPrimitive?.booleanOrNull ?: false,
+        email: String = data.getOrElse("email"){ null }?.jsonPrimitive?.contentOrNull ?: ""
+    ): this(
+        app = app,
+        isAnonymous = isAnonymous,
+        uid = data["uid"]?.jsonPrimitive?.contentOrNull ?: data["user_id"]?.jsonPrimitive?.contentOrNull ?: data["localId"]?.jsonPrimitive?.contentOrNull ?: "",
+        idToken = data["idToken"]?.jsonPrimitive?.contentOrNull ?: data.getValue("id_token").jsonPrimitive.content,
+        refreshToken = data["refreshToken"]?.jsonPrimitive?.contentOrNull ?: data.getValue("refresh_token").jsonPrimitive.content,
+        expiresIn = data["expiresIn"]?.jsonPrimitive?.intOrNull ?: data.getValue("expires_in").jsonPrimitive.int,
+        createdAt = data["createdAt"]?.jsonPrimitive?.longOrNull ?: System.currentTimeMillis(),
+        email = email
     )
 
     val claims: Map<String, Any?> by lazy {
@@ -385,7 +392,7 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
                     signOutAndThrowInvalidUserException(body.orEmpty(), "token API returned an error: $body")
                 } else {
                     jsonParser.parseToJsonElement(body!!).jsonObject.apply {
-                        val user = FirebaseUserImpl(app, this, user.isAnonymous)
+                        val user = FirebaseUserImpl(app, this, user.isAnonymous, user.email)
                         if (user.claims["aud"] != app.options.projectId) {
                             signOutAndThrowInvalidUserException(
                                 user.claims.toString(),
diff --git a/src/main/java/com/google/firebase/auth/FirebaseUser.kt b/src/main/java/com/google/firebase/auth/FirebaseUser.kt
index 3f61dea..c76c62b 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseUser.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseUser.kt
@@ -7,8 +7,8 @@ abstract class FirebaseUser {
     abstract val isAnonymous: Boolean
     abstract fun delete(): Task<Void>
     abstract fun reload(): Task<Void>
+    abstract val email: String
 
-    val email: String get() = TODO()
     val displayName: String get() = TODO()
     val phoneNumber: String get() = TODO()
     val photoUrl: String? get() = TODO()
diff --git a/src/test/kotlin/FirebaseAuthTest.kt b/src/test/kotlin/FirebaseAuthTest.kt
new file mode 100644
index 0000000..68b1574
--- /dev/null
+++ b/src/test/kotlin/FirebaseAuthTest.kt
@@ -0,0 +1,68 @@
+import android.app.Application
+import com.google.firebase.Firebase
+import com.google.firebase.FirebaseOptions
+import com.google.firebase.FirebasePlatform
+import com.google.firebase.auth.FirebaseAuth
+import com.google.firebase.firestore.firestore
+import com.google.firebase.initialize
+import kotlinx.coroutines.tasks.await
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Before
+import org.junit.Test
+import java.io.File
+import kotlin.random.Random
+
+internal class FirebaseAuthTest: FirebaseTest() {
+
+    private lateinit var auth: FirebaseAuth
+
+    @Before
+    fun initialize() {
+        FirebasePlatform.initializeFirebasePlatform(object : FirebasePlatform() {
+            val storage = mutableMapOf<String, String>()
+            override fun store(key: String, value: String) = storage.set(key, value)
+            override fun retrieve(key: String) = storage[key]
+            override fun clear(key: String) { storage.remove(key) }
+            override fun log(msg: String) = println(msg)
+            override fun getDatabasePath(name: String) = File("./build/$name")
+        })
+        val options = FirebaseOptions.Builder()
+            .setProjectId("my-firebase-project")
+            .setApplicationId("1:27992087142:android:ce3b6448250083d1")
+            .setApiKey("AIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
+            // setDatabaseURL(...)
+            // setStorageBucket(...)
+            .build()
+        val firebaseApp = Firebase.initialize(Application(), options)
+        auth = FirebaseAuth.getInstance(app = firebaseApp)
+    }
+
+    @Test
+    fun testCreateUserWithEmailAndPassword() = runTest {
+        val email = "test+${Random.nextInt(100000)}@test.com"
+        val createResult = auth.createUserWithEmailAndPassword(
+            email,
+            "test123"
+        ).await()
+        assertNotEquals(null, createResult.user?.uid)
+        //assertEquals(null, createResult.user?.displayName)
+        //assertEquals(null, createResult.user?.phoneNumber)
+        assertEquals(false, createResult.user?.isAnonymous)
+        assertEquals(email, createResult.user?.email)
+
+        val signInResult = auth.signInWithEmailAndPassword(email, "test123").await()
+        assertEquals(createResult.user?.uid, signInResult.user?.uid)
+
+        signInResult.user!!.delete()
+    }
+
+    @Test
+    fun testSignInAnonymously() = runTest {
+        val signInResult = auth.signInAnonymously().await()
+        assertEquals(true, signInResult.user?.isAnonymous)
+        signInResult.user!!.delete()
+    }
+}
\ No newline at end of file

From 1ac38d7151b45a954840afa921f788752c2f13fe Mon Sep 17 00:00:00 2001
From: Jakub Kostka <jacob.cube.mexx@gmail.com>
Date: Mon, 30 Sep 2024 09:58:23 +0200
Subject: [PATCH 03/23] "File must end with a newline (\n)" fix

---
 src/test/kotlin/FirebaseAuthTest.kt | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/test/kotlin/FirebaseAuthTest.kt b/src/test/kotlin/FirebaseAuthTest.kt
index 68b1574..6211481 100644
--- a/src/test/kotlin/FirebaseAuthTest.kt
+++ b/src/test/kotlin/FirebaseAuthTest.kt
@@ -3,11 +3,9 @@ import com.google.firebase.Firebase
 import com.google.firebase.FirebaseOptions
 import com.google.firebase.FirebasePlatform
 import com.google.firebase.auth.FirebaseAuth
-import com.google.firebase.firestore.firestore
 import com.google.firebase.initialize
 import kotlinx.coroutines.tasks.await
 import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertArrayEquals
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
 import org.junit.Before
@@ -17,7 +15,7 @@ import kotlin.random.Random
 
 internal class FirebaseAuthTest: FirebaseTest() {
 
-    private lateinit var auth: FirebaseAuth
+    private lateinit var auth : FirebaseAuth
 
     @Before
     fun initialize() {
@@ -65,4 +63,4 @@ internal class FirebaseAuthTest: FirebaseTest() {
         assertEquals(true, signInResult.user?.isAnonymous)
         signInResult.user!!.delete()
     }
-}
\ No newline at end of file
+}

From 6e663f0171eb1176367d35e5825576517706abed Mon Sep 17 00:00:00 2001
From: nbransby <nbransby@gmail.com>
Date: Sat, 5 Oct 2024 13:28:47 +0800
Subject: [PATCH 04/23] ktlintFormat

---
 .../java/com/google/firebase/auth/FirebaseAuth.kt  | 14 ++++----------
 src/test/kotlin/FirebaseAuthTest.kt                |  8 ++++----
 2 files changed, 8 insertions(+), 14 deletions(-)

diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index 95bd2e7..152963e 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -29,15 +29,9 @@ import kotlinx.serialization.json.intOrNull
 import kotlinx.serialization.json.jsonObject
 import kotlinx.serialization.json.jsonPrimitive
 import kotlinx.serialization.json.longOrNull
-import okhttp3.Call
-import okhttp3.Callback
-import okhttp3.MediaType
-import okhttp3.OkHttpClient
-import okhttp3.Request
-import okhttp3.RequestBody
-import okhttp3.Response
+import okhttp3.*
 import java.io.IOException
-import java.util.Base64
+import java.util.*
 import java.util.concurrent.CopyOnWriteArrayList
 import java.util.concurrent.TimeUnit
 
@@ -60,8 +54,8 @@ class FirebaseUserImpl private constructor(
         app: FirebaseApp,
         data: JsonObject,
         isAnonymous: Boolean = data["isAnonymous"]?.jsonPrimitive?.booleanOrNull ?: false,
-        email: String = data.getOrElse("email"){ null }?.jsonPrimitive?.contentOrNull ?: ""
-    ): this(
+        email: String = data.getOrElse("email") { null }?.jsonPrimitive?.contentOrNull ?: ""
+    ) : this(
         app = app,
         isAnonymous = isAnonymous,
         uid = data["uid"]?.jsonPrimitive?.contentOrNull ?: data["user_id"]?.jsonPrimitive?.contentOrNull ?: data["localId"]?.jsonPrimitive?.contentOrNull ?: "",
diff --git a/src/test/kotlin/FirebaseAuthTest.kt b/src/test/kotlin/FirebaseAuthTest.kt
index 6211481..6e8965a 100644
--- a/src/test/kotlin/FirebaseAuthTest.kt
+++ b/src/test/kotlin/FirebaseAuthTest.kt
@@ -13,9 +13,9 @@ import org.junit.Test
 import java.io.File
 import kotlin.random.Random
 
-internal class FirebaseAuthTest: FirebaseTest() {
+internal class FirebaseAuthTest : FirebaseTest() {
 
-    private lateinit var auth : FirebaseAuth
+    private lateinit var auth: FirebaseAuth
 
     @Before
     fun initialize() {
@@ -46,8 +46,8 @@ internal class FirebaseAuthTest: FirebaseTest() {
             "test123"
         ).await()
         assertNotEquals(null, createResult.user?.uid)
-        //assertEquals(null, createResult.user?.displayName)
-        //assertEquals(null, createResult.user?.phoneNumber)
+        // assertEquals(null, createResult.user?.displayName)
+        // assertEquals(null, createResult.user?.phoneNumber)
         assertEquals(false, createResult.user?.isAnonymous)
         assertEquals(email, createResult.user?.email)
 

From ab943b6d7b8b252940da9fdaaefac774d29803c3 Mon Sep 17 00:00:00 2001
From: nbransby <nbransby@gmail.com>
Date: Sat, 5 Oct 2024 13:47:29 +0800
Subject: [PATCH 05/23] remove * imports

---
 src/main/java/com/google/firebase/auth/FirebaseAuth.kt | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index 152963e..ee51e32 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -29,9 +29,15 @@ import kotlinx.serialization.json.intOrNull
 import kotlinx.serialization.json.jsonObject
 import kotlinx.serialization.json.jsonPrimitive
 import kotlinx.serialization.json.longOrNull
-import okhttp3.*
+import okhttp3.Call
+import okhttp3.Callback
+import okhttp3.MediaType
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.RequestBody
+import okhttp3.Response
 import java.io.IOException
-import java.util.*
+import java.util.Base64
 import java.util.concurrent.CopyOnWriteArrayList
 import java.util.concurrent.TimeUnit
 

From d4967982ddcc4ec83bcfc22da542c892333efb11 Mon Sep 17 00:00:00 2001
From: Jakub Kostka <jacob.cube.mexx@gmail.com>
Date: Mon, 21 Oct 2024 11:45:12 +0200
Subject: [PATCH 06/23] correct mock Firebase project implementation

---
 src/test/kotlin/FirebaseAuthTest.kt | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/test/kotlin/FirebaseAuthTest.kt b/src/test/kotlin/FirebaseAuthTest.kt
index 6211481..ccbaff3 100644
--- a/src/test/kotlin/FirebaseAuthTest.kt
+++ b/src/test/kotlin/FirebaseAuthTest.kt
@@ -28,12 +28,14 @@ internal class FirebaseAuthTest: FirebaseTest() {
             override fun getDatabasePath(name: String) = File("./build/$name")
         })
         val options = FirebaseOptions.Builder()
-            .setProjectId("my-firebase-project")
-            .setApplicationId("1:27992087142:android:ce3b6448250083d1")
-            .setApiKey("AIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
-            // setDatabaseURL(...)
-            // setStorageBucket(...)
+            .setProjectId("fir-java-sdk")
+            .setApplicationId("1:341458593155:web:bf8e1aa37efe01f32d42b6")
+            .setApiKey("AIzaSyCvVHjTJHyeStnzIE7J9LLtHqWk6reGM08")
+            .setDatabaseUrl("https://fir-java-sdk-default-rtdb.firebaseio.com")
+            .setStorageBucket("fir-java-sdk.appspot.com")
+            .setGcmSenderId("341458593155")
             .build()
+
         val firebaseApp = Firebase.initialize(Application(), options)
         auth = FirebaseAuth.getInstance(app = firebaseApp)
     }

From e8a723e291d612b6806d3fdae44e6af790496b87 Mon Sep 17 00:00:00 2001
From: Jakub Kostka <jacob.cube.mexx@gmail.com>
Date: Mon, 21 Oct 2024 15:53:14 +0200
Subject: [PATCH 07/23] updateEmail and verifyBeforeUpdateEmail implementation
 email is nullable again, check for empty string cancelation of token
 refresher after signout

---
 .../com/google/firebase/auth/FirebaseAuth.kt  | 332 +++++++++++-------
 .../com/google/firebase/auth/FirebaseUser.kt  |   6 +-
 .../google/firebase/auth/OobRequestType.kt    |   8 +
 src/test/kotlin/FirebaseAuthTest.kt           |  21 +-
 4 files changed, 234 insertions(+), 133 deletions(-)
 create mode 100644 src/main/java/com/google/firebase/auth/OobRequestType.kt

diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index ee51e32..0e94f5f 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -41,10 +41,10 @@ import java.util.Base64
 import java.util.concurrent.CopyOnWriteArrayList
 import java.util.concurrent.TimeUnit
 
-val jsonParser = Json { ignoreUnknownKeys = true }
+internal val jsonParser = Json { ignoreUnknownKeys = true }
 
 @Serializable
-class FirebaseUserImpl private constructor(
+class FirebaseUserImpl internal constructor(
     @Transient
     private val app: FirebaseApp = FirebaseApp.getInstance(),
     override val isAnonymous: Boolean,
@@ -53,14 +53,14 @@ class FirebaseUserImpl private constructor(
     val refreshToken: String,
     val expiresIn: Int,
     val createdAt: Long,
-    override val email: String
+    override val email: String?
 ) : FirebaseUser() {
 
     constructor(
         app: FirebaseApp,
         data: JsonObject,
         isAnonymous: Boolean = data["isAnonymous"]?.jsonPrimitive?.booleanOrNull ?: false,
-        email: String = data.getOrElse("email") { null }?.jsonPrimitive?.contentOrNull ?: ""
+        email: String? = data.getOrElse("email") { null }?.jsonPrimitive?.contentOrNull
     ) : this(
         app = app,
         isAnonymous = isAnonymous,
@@ -120,24 +120,109 @@ class FirebaseUserImpl private constructor(
         return source.task
     }
 
+    override fun updateEmail(email: String): Task<Unit> = FirebaseAuth.getInstance(app).updateEmail(email)
+
     override fun reload(): Task<Void> {
         val source = TaskCompletionSource<Void>()
         FirebaseAuth.getInstance(app).refreshToken(this, source) { null }
         return source.task
     }
 
+    override fun verifyBeforeUpdateEmail(
+        newEmail: String,
+        actionCodeSettings: ActionCodeSettings?
+    ): Task<Unit> {
+        val source = TaskCompletionSource<Unit>()
+        val body = RequestBody.create(
+            FirebaseAuth.getInstance(app).json,
+            JsonObject(
+                mapOf(
+                    "idToken" to JsonPrimitive(idToken),
+                    "email" to JsonPrimitive(email),
+                    "newEmail" to JsonPrimitive(newEmail),
+                    "requestType" to JsonPrimitive(OobRequestType.VERIFY_AND_CHANGE_EMAIL.name)
+                )
+            ).toString()
+        )
+        val request = Request.Builder()
+            .url("https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=" + app.options.apiKey)
+            .post(body)
+            .build()
+        FirebaseAuth.getInstance(app).client.newCall(request).enqueue(object : Callback {
+
+            override fun onFailure(call: Call, e: IOException) {
+                source.setException(FirebaseException(e.toString(), e))
+                e.printStackTrace()
+            }
+
+            @Throws(IOException::class)
+            override fun onResponse(call: Call, response: Response) {
+                if (!response.isSuccessful) {
+                    FirebaseAuth.getInstance(app).signOut()
+                    source.setException(
+                        FirebaseAuth.getInstance(app).createAuthInvalidUserException(
+                            "verifyEmail",
+                            request,
+                            response
+                        )
+                    )
+                } else {
+                    source.setResult(null)
+                }
+            }
+        })
+        return source.task
+    }
+
     override fun getIdToken(forceRefresh: Boolean) = FirebaseAuth.getInstance(app).getAccessToken(forceRefresh)
 }
 
 class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
 
-    val json = MediaType.parse("application/json; charset=utf-8")
-    val client: OkHttpClient = OkHttpClient.Builder()
+    internal val json = MediaType.parse("application/json; charset=utf-8")
+    internal val client: OkHttpClient = OkHttpClient.Builder()
         .connectTimeout(60, TimeUnit.SECONDS)
         .readTimeout(60, TimeUnit.SECONDS)
         .writeTimeout(60, TimeUnit.SECONDS)
         .build()
 
+    private fun enqueueAuthPost(
+        url: String,
+        body: RequestBody,
+        setResult: (responseBody: String) -> FirebaseUserImpl?
+    ): TaskCompletionSource<AuthResult> {
+        val source = TaskCompletionSource<AuthResult>()
+        val request = Request.Builder()
+            .url("$url?key=" + app.options.apiKey)
+            .post(body)
+            .build()
+
+        client.newCall(request).enqueue(object : Callback {
+            override fun onFailure(call: Call, e: IOException) {
+                source.setException(FirebaseException(e.toString(), e))
+            }
+
+            @Throws(IOException::class)
+            override fun onResponse(call: Call, response: Response) {
+                if (!response.isSuccessful) {
+                    source.setException(
+                        createAuthInvalidUserException("accounts", request, response)
+                    )
+                } else {
+                    if(response.body()?.use { it.string() }?.also { responseBody ->
+                        user = setResult(responseBody)
+                        source.setResult(AuthResult { user })
+                    } == null) {
+                        source.setException(
+                            createAuthInvalidUserException("accounts", request, response)
+                        )
+                    }
+                }
+            }
+        })
+        return source
+    }
+
     companion object {
 
         @JvmStatic
@@ -145,6 +230,8 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
 
         @JvmStatic
         fun getInstance(app: FirebaseApp): FirebaseAuth = app.get(FirebaseAuth::class.java)
+
+        private const val REFRESH_TOKEN_TAG = "refresh_token_tag"
     }
 
     private val internalIdTokenListeners = CopyOnWriteArrayList<com.google.firebase.auth.internal.IdTokenListener>()
@@ -192,127 +279,67 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
         }
 
     fun signInAnonymously(): Task<AuthResult> {
-        val source = TaskCompletionSource<AuthResult>()
-        val body = RequestBody.create(json, JsonObject(mapOf("returnSecureToken" to JsonPrimitive(true))).toString())
-        val request = Request.Builder()
-            .url("https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=" + app.options.apiKey)
-            .post(body)
-            .build()
-        client.newCall(request).enqueue(object : Callback {
-
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
+        val source = enqueueAuthPost(
+            url = "https://identitytoolkit.googleapis.com/v1/accounts:signUp",
+            body = RequestBody.create(json, JsonObject(mapOf("returnSecureToken" to JsonPrimitive(true))).toString()),
+            setResult = { responseBody ->
+                FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject, isAnonymous = true)
             }
-
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    source.setException(
-                        createAuthInvalidUserException("accounts:signUp", request, response)
-                    )
-                } else {
-                    val body = response.body()!!.use { it.string() }
-                    user = FirebaseUserImpl(app, jsonParser.parseToJsonElement(body).jsonObject, true)
-                    source.setResult(AuthResult { user })
-                }
-            }
-        })
+        )
         return source.task
     }
 
     fun signInWithCustomToken(customToken: String): Task<AuthResult> {
-        val source = TaskCompletionSource<AuthResult>()
-        val body = RequestBody.create(
-            json,
-            JsonObject(mapOf("token" to JsonPrimitive(customToken), "returnSecureToken" to JsonPrimitive(true))).toString()
-        )
-        val request = Request.Builder()
-            .url("https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken?key=" + app.options.apiKey)
-            .post(body)
-            .build()
-        client.newCall(request).enqueue(object : Callback {
-
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
+        val source = enqueueAuthPost(
+            url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken",
+            body = RequestBody.create(
+                json,
+                JsonObject(mapOf("token" to JsonPrimitive(customToken), "returnSecureToken" to JsonPrimitive(true))).toString()
+            ),
+            setResult = { responseBody ->
+                FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
             }
-
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    source.setException(
-                        createAuthInvalidUserException("verifyCustomToken", request, response)
-                    )
-                } else {
-                    val body = response.body()!!.use { it.string() }
-                    val user = FirebaseUserImpl(app, jsonParser.parseToJsonElement(body).jsonObject)
-                    refreshToken(user, source) { AuthResult { it } }
-                }
-            }
-        })
+        )
         return source.task
     }
 
     fun createUserWithEmailAndPassword(email: String, password: String): Task<AuthResult> {
-        val source = TaskCompletionSource<AuthResult>()
-        val body = RequestBody.create(
-            json,
-            JsonObject(mapOf("email" to JsonPrimitive(email), "password" to JsonPrimitive(password), "returnSecureToken" to JsonPrimitive(true))).toString()
-        )
-        val request = Request.Builder()
-            .url("https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=" + app.options.apiKey)
-            .post(body)
-            .build()
-        client.newCall(request).enqueue(object : Callback {
-
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
-            }
-
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    source.setException(
-                        createAuthInvalidUserException("signupNewUser", request, response)
+        val source = enqueueAuthPost(
+            url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser",
+            body = RequestBody.create(
+                json,
+                JsonObject(
+                    mapOf(
+                        "email" to JsonPrimitive(email),
+                        "password" to JsonPrimitive(password),
+                        "returnSecureToken" to JsonPrimitive(true)
                     )
-                } else {
-                    val responseBody = response.body()?.use { it.string() } ?: ""
-                    val user = FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
-                    refreshToken(user, source) { AuthResult { it } }
-                }
+                ).toString()
+            ),
+            setResult = { responseBody ->
+                FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
             }
-        })
+        )
         return source.task
     }
 
     fun signInWithEmailAndPassword(email: String, password: String): Task<AuthResult> {
-        val source = TaskCompletionSource<AuthResult>()
-        val body = RequestBody.create(
-            json,
-            JsonObject(mapOf("email" to JsonPrimitive(email), "password" to JsonPrimitive(password), "returnSecureToken" to JsonPrimitive(true))).toString()
-        )
-        val request = Request.Builder()
-            .url("https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=" + app.options.apiKey)
-            .post(body)
-            .build()
-        client.newCall(request).enqueue(object : Callback {
-
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
-            }
-
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    source.setException(
-                        createAuthInvalidUserException("verifyPassword", request, response)
+        val source = enqueueAuthPost(
+            url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword",
+            body = RequestBody.create(
+                json,
+                JsonObject(
+                    mapOf(
+                        "email" to JsonPrimitive(email),
+                        "password" to JsonPrimitive(password),
+                        "returnSecureToken" to JsonPrimitive(true)
                     )
-                } else {
-                    val body = response.body()!!.use { it.string() }
-                    val user = FirebaseUserImpl(app, jsonParser.parseToJsonElement(body).jsonObject)
-                    refreshToken(user, source) { AuthResult { it } }
-                }
+                ).toString()
+            ),
+            setResult = { responseBody ->
+                FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
             }
-        })
+        )
         return source.task
     }
 
@@ -336,7 +363,10 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
     }
 
     fun signOut() {
-        // todo cancel token refresher
+        // cancel token refresher
+        client.dispatcher().queuedCalls().find { it.request().tag() == REFRESH_TOKEN_TAG }?.cancel() ?: {
+            client.dispatcher().runningCalls().find { it.request().tag() == REFRESH_TOKEN_TAG }?.cancel()
+        }
         user = null
     }
 
@@ -377,6 +407,7 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
         val request = Request.Builder()
             .url("https://securetoken.googleapis.com/v1/token?key=" + app.options.apiKey)
             .post(body)
+            .tag(REFRESH_TOKEN_TAG)
             .build()
 
         client.newCall(request).enqueue(object : Callback {
@@ -387,20 +418,21 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
 
             @Throws(IOException::class)
             override fun onResponse(call: Call, response: Response) {
-                val body = response.body()?.use { it.string() }
-                if (!response.isSuccessful) {
-                    signOutAndThrowInvalidUserException(body.orEmpty(), "token API returned an error: $body")
+                val responseBody = response.body()?.use { it.string() }
+
+                if (!response.isSuccessful || responseBody == null) {
+                    signOutAndThrowInvalidUserException(responseBody.orEmpty(), "token API returned an error: $body")
                 } else {
-                    jsonParser.parseToJsonElement(body!!).jsonObject.apply {
-                        val user = FirebaseUserImpl(app, this, user.isAnonymous, user.email)
-                        if (user.claims["aud"] != app.options.projectId) {
+                    jsonParser.parseToJsonElement(responseBody).jsonObject.apply {
+                        val newUser = FirebaseUserImpl(app, this, user.isAnonymous, user.email)
+                        if (newUser.claims["aud"] != app.options.projectId) {
                             signOutAndThrowInvalidUserException(
-                                user.claims.toString(),
-                                "Project ID's do not match ${user.claims["aud"]} != ${app.options.projectId}"
+                                newUser.claims.toString(),
+                                "Project ID's do not match ${newUser.claims["aud"]} != ${app.options.projectId}"
                             )
                         } else {
-                            this@FirebaseAuth.user = user
-                            source.setResult(user)
+                            this@FirebaseAuth.user = newUser
+                            source.setResult(newUser)
                         }
                     }
                 }
@@ -414,6 +446,65 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
         return source
     }
 
+    internal fun updateEmail(email: String): Task<Unit> {
+        val source = TaskCompletionSource<Unit>()
+
+        val body = RequestBody.create(
+            json,
+            JsonObject(
+                mapOf(
+                    "idToken" to JsonPrimitive(user?.idToken),
+                    "email" to JsonPrimitive(email),
+                    "returnSecureToken" to JsonPrimitive(true)
+                )
+            ).toString()
+        )
+        val request = Request.Builder()
+            .url("https://identitytoolkit.googleapis.com/v1/accounts:update?key=" + app.options.apiKey)
+            .post(body)
+            .build()
+
+        client.newCall(request).enqueue(object : Callback {
+
+            override fun onFailure(call: Call, e: IOException) {
+                source.setException(FirebaseException(e.toString(), e))
+            }
+
+            @Throws(IOException::class)
+            override fun onResponse(call: Call, response: Response) {
+                if (!response.isSuccessful) {
+                    signOut()
+                    source.setException(
+                        createAuthInvalidUserException(
+                            "updateEmail",
+                            request,
+                            response
+                        )
+                    )
+                } else {
+                    val newBody = jsonParser.parseToJsonElement(
+                        response.body()?.use { it.string() } ?: ""
+                    ).jsonObject
+
+                    user?.let { prev ->
+                        user = FirebaseUserImpl(
+                            app = app,
+                            isAnonymous = prev.isAnonymous,
+                            uid = prev.uid,
+                            idToken = newBody["idToken"]?.jsonPrimitive?.contentOrNull ?: prev.idToken,
+                            refreshToken = newBody["refreshToken"]?.jsonPrimitive?.contentOrNull ?: prev.refreshToken,
+                            expiresIn = newBody["expiresIn"]?.jsonPrimitive?.intOrNull ?: prev.expiresIn,
+                            createdAt = prev.createdAt,
+                            email = newBody["newEmail"]?.jsonPrimitive?.contentOrNull ?: prev.email
+                        )
+                    }
+                    source.setResult(null)
+                }
+            }
+        })
+        return source.task
+    }
+
     override fun getUid(): String? {
         return user?.uid
     }
@@ -464,7 +555,6 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
     }
 
     fun sendPasswordResetEmail(email: String, settings: ActionCodeSettings?): Task<Unit> = TODO()
-    fun signInWithCredential(authCredential: AuthCredential): Task<AuthResult> = TODO()
     fun checkActionCode(code: String): Task<ActionCodeResult> = TODO()
     fun confirmPasswordReset(code: String, newPassword: String): Task<Unit> = TODO()
     fun fetchSignInMethodsForEmail(email: String): Task<SignInMethodQueryResult> = TODO()
diff --git a/src/main/java/com/google/firebase/auth/FirebaseUser.kt b/src/main/java/com/google/firebase/auth/FirebaseUser.kt
index c76c62b..50e4e52 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseUser.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseUser.kt
@@ -4,10 +4,12 @@ import com.google.android.gms.tasks.Task
 
 abstract class FirebaseUser {
     abstract val uid: String
+    abstract val email: String?
     abstract val isAnonymous: Boolean
     abstract fun delete(): Task<Void>
     abstract fun reload(): Task<Void>
-    abstract val email: String
+    abstract fun verifyBeforeUpdateEmail(newEmail: String, actionCodeSettings: ActionCodeSettings?): Task<Unit>
+    abstract fun updateEmail(email: String): Task<Unit>
 
     val displayName: String get() = TODO()
     val phoneNumber: String get() = TODO()
@@ -22,11 +24,9 @@ abstract class FirebaseUser {
     fun sendEmailVerification(): Task<Unit> = TODO()
     fun sendEmailVerification(actionCodeSettings: ActionCodeSettings): Task<Unit> = TODO()
     fun unlink(provider: String): Task<AuthResult> = TODO()
-    fun updateEmail(email: String): Task<Unit> = TODO()
     fun updatePassword(password: String): Task<Unit> = TODO()
     fun updatePhoneNumber(credential: AuthCredential): Task<Unit> = TODO()
     fun updateProfile(request: UserProfileChangeRequest): Task<Unit> = TODO()
-    fun verifyBeforeUpdateEmail(newEmail: String, actionCodeSettings: ActionCodeSettings?): Task<Unit> = TODO()
     fun reauthenticate(credential: AuthCredential): Task<Unit> = TODO()
     fun reauthenticateAndRetrieveData(credential: AuthCredential): Task<AuthResult> = TODO()
 }
diff --git a/src/main/java/com/google/firebase/auth/OobRequestType.kt b/src/main/java/com/google/firebase/auth/OobRequestType.kt
new file mode 100644
index 0000000..f0c31c3
--- /dev/null
+++ b/src/main/java/com/google/firebase/auth/OobRequestType.kt
@@ -0,0 +1,8 @@
+package com.google.firebase.auth
+
+internal enum class OobRequestType {
+    PASSWORD_RESET,
+    EMAIL_SIGNIN,
+    VERIFY_EMAIL,
+    VERIFY_AND_CHANGE_EMAIL
+}
\ No newline at end of file
diff --git a/src/test/kotlin/FirebaseAuthTest.kt b/src/test/kotlin/FirebaseAuthTest.kt
index 2925db4..029cd11 100644
--- a/src/test/kotlin/FirebaseAuthTest.kt
+++ b/src/test/kotlin/FirebaseAuthTest.kt
@@ -1,3 +1,4 @@
+
 import android.app.Application
 import com.google.firebase.Firebase
 import com.google.firebase.FirebaseOptions
@@ -6,12 +7,13 @@ import com.google.firebase.auth.FirebaseAuth
 import com.google.firebase.initialize
 import kotlinx.coroutines.tasks.await
 import kotlinx.coroutines.test.runTest
+import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
 import org.junit.Before
 import org.junit.Test
 import java.io.File
-import kotlin.random.Random
+import java.util.UUID
 
 internal class FirebaseAuthTest : FirebaseTest() {
 
@@ -40,29 +42,30 @@ internal class FirebaseAuthTest : FirebaseTest() {
         auth = FirebaseAuth.getInstance(app = firebaseApp)
     }
 
+    @After
+    fun clear() {
+        auth.currentUser?.delete()
+    }
+
     @Test
     fun testCreateUserWithEmailAndPassword() = runTest {
-        val email = "test+${Random.nextInt(100000)}@test.com"
-        val createResult = auth.createUserWithEmailAndPassword(
-            email,
-            "test123"
-        ).await()
+        val email = "test+${UUID.randomUUID()}@test.com"
+        val createResult = auth.createUserWithEmailAndPassword(email, "test123").await()
         assertNotEquals(null, createResult.user?.uid)
         // assertEquals(null, createResult.user?.displayName)
         // assertEquals(null, createResult.user?.phoneNumber)
         assertEquals(false, createResult.user?.isAnonymous)
         assertEquals(email, createResult.user?.email)
+        assertNotEquals("", createResult.user!!.email)
 
         val signInResult = auth.signInWithEmailAndPassword(email, "test123").await()
         assertEquals(createResult.user?.uid, signInResult.user?.uid)
-
-        signInResult.user!!.delete()
     }
 
     @Test
     fun testSignInAnonymously() = runTest {
         val signInResult = auth.signInAnonymously().await()
+        assertNotEquals("", signInResult.user!!.email)
         assertEquals(true, signInResult.user?.isAnonymous)
-        signInResult.user!!.delete()
     }
 }

From e51997d384cafe9024eae7e59a73e9d544c77306 Mon Sep 17 00:00:00 2001
From: Jakub Kostka <jacob.cube.mexx@gmail.com>
Date: Mon, 21 Oct 2024 16:02:26 +0200
Subject: [PATCH 08/23] added todo

---
 src/main/java/com/google/firebase/auth/FirebaseAuth.kt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index 0e94f5f..ba7543b 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -128,6 +128,7 @@ class FirebaseUserImpl internal constructor(
         return source.task
     }
 
+    //TODO implement ActionCodeSettings and pass it to the url
     override fun verifyBeforeUpdateEmail(
         newEmail: String,
         actionCodeSettings: ActionCodeSettings?

From 25efe1cfd9e6d76db84688f8e7cae50b9e95d350 Mon Sep 17 00:00:00 2001
From: Jakub Kostka <jacob.cube.mexx@gmail.com>
Date: Thu, 7 Nov 2024 16:11:35 +0100
Subject: [PATCH 09/23] ktlint formatting + update of user data after
 signInWithCustomToken + photoUrl and displayName implementation

---
 src/main/java/android/net/Uri.kt              |   7 +-
 .../com/google/firebase/auth/FirebaseAuth.kt  | 808 ++++++++++++------
 .../com/google/firebase/auth/FirebaseUser.kt  |  27 +-
 .../google/firebase/auth/OobRequestType.kt    |   2 +-
 .../auth/UserProfileChangeRequest.java        |  18 -
 .../firebase/auth/UserProfileChangeRequest.kt |  47 +
 src/test/kotlin/AppTest.kt                    |  42 +-
 src/test/kotlin/AuthTest.kt                   |  92 +-
 src/test/kotlin/FirebaseAuthTest.kt           | 100 +--
 src/test/kotlin/FirebaseTest.kt               |  67 +-
 10 files changed, 814 insertions(+), 396 deletions(-)
 delete mode 100644 src/main/java/com/google/firebase/auth/UserProfileChangeRequest.java
 create mode 100644 src/main/java/com/google/firebase/auth/UserProfileChangeRequest.kt

diff --git a/src/main/java/android/net/Uri.kt b/src/main/java/android/net/Uri.kt
index 851faa2..c14bc76 100644
--- a/src/main/java/android/net/Uri.kt
+++ b/src/main/java/android/net/Uri.kt
@@ -3,13 +3,16 @@ package android.net
 import java.net.URI
 import java.util.Collections
 
-class Uri(private val uri: URI) {
-
+class Uri(
+    private val uri: URI,
+) {
     companion object {
         @JvmStatic
         fun parse(uriString: String) = Uri(URI.create(uriString))
     }
 
+    override fun toString(): String = uri.toString()
+
     val scheme get() = uri.scheme
     val port get() = uri.port
     val host get() = uri.host
diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index 8442c1e..5492c67 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -1,5 +1,6 @@
 package com.google.firebase.auth
 
+import android.net.Uri
 import android.util.Log
 import com.google.android.gms.tasks.Task
 import com.google.android.gms.tasks.TaskCompletionSource
@@ -45,11 +46,9 @@ internal val jsonParser = Json { ignoreUnknownKeys = true }
 
 class UrlFactory(
     private val app: FirebaseApp,
-    private val emulatorUrl: String? = null
+    private val emulatorUrl: String? = null,
 ) {
-    fun buildUrl(uri: String): String {
-        return "${emulatorUrl ?: "https://"}$uri?key=${app.options.apiKey}"
-    }
+    fun buildUrl(uri: String): String = "${emulatorUrl ?: "https://"}$uri?key=${app.options.apiKey}"
 }
 
 @Serializable
@@ -63,26 +62,34 @@ class FirebaseUserImpl internal constructor(
     val expiresIn: Int,
     val createdAt: Long,
     override val email: String?,
+    override val photoUrl: String?,
+    override val displayName: String?,
     @Transient
-    private val urlFactory: UrlFactory = UrlFactory(app)
+    private val urlFactory: UrlFactory = UrlFactory(app),
 ) : FirebaseUser() {
-
     constructor(
         app: FirebaseApp,
         data: JsonObject,
         isAnonymous: Boolean = data["isAnonymous"]?.jsonPrimitive?.booleanOrNull ?: false,
         email: String? = data.getOrElse("email") { null }?.jsonPrimitive?.contentOrNull,
-        urlFactory: UrlFactory = UrlFactory(app)
+        photoUrl: String? = data.getOrElse("photoUrl") { null }?.jsonPrimitive?.contentOrNull,
+        displayName: String? = data.getOrElse("displayName") { null }?.jsonPrimitive?.contentOrNull,
+        urlFactory: UrlFactory = UrlFactory(app),
     ) : this(
         app = app,
         isAnonymous = isAnonymous,
-        uid = data["uid"]?.jsonPrimitive?.contentOrNull ?: data["user_id"]?.jsonPrimitive?.contentOrNull ?: data["localId"]?.jsonPrimitive?.contentOrNull ?: "",
+        uid =
+            data["uid"]?.jsonPrimitive?.contentOrNull ?: data["user_id"]?.jsonPrimitive?.contentOrNull
+                ?: data["localId"]?.jsonPrimitive?.contentOrNull
+                ?: "",
         idToken = data["idToken"]?.jsonPrimitive?.contentOrNull ?: data.getValue("id_token").jsonPrimitive.content,
         refreshToken = data["refreshToken"]?.jsonPrimitive?.contentOrNull ?: data.getValue("refresh_token").jsonPrimitive.content,
         expiresIn = data["expiresIn"]?.jsonPrimitive?.intOrNull ?: data.getValue("expires_in").jsonPrimitive.int,
         createdAt = data["createdAt"]?.jsonPrimitive?.longOrNull ?: System.currentTimeMillis(),
         email = email,
-        urlFactory = urlFactory
+        photoUrl = photoUrl ?: data["photo_url"]?.jsonPrimitive?.contentOrNull,
+        displayName = displayName ?: data["display_name"]?.jsonPrimitive?.contentOrNull,
+        urlFactory = urlFactory,
     )
 
     internal val claims: Map<String, Any?> by lazy {
@@ -93,43 +100,53 @@ class FirebaseUserImpl internal constructor(
             .orEmpty()
     }
 
-    internal val JsonElement.value get(): Any? = when (this) {
-        is JsonNull -> null
-        is JsonArray -> map { it.value }
-        is JsonObject -> jsonObject.mapValues { (_, it) -> it.value }
-        is JsonPrimitive -> booleanOrNull ?: doubleOrNull ?: content
-        else -> TODO()
-    }
+    internal val JsonElement.value get(): Any? =
+        when (this) {
+            is JsonNull -> null
+            is JsonArray -> map { it.value }
+            is JsonObject -> jsonObject.mapValues { (_, it) -> it.value }
+            is JsonPrimitive -> booleanOrNull ?: doubleOrNull ?: content
+            else -> TODO()
+        }
 
     override fun delete(): Task<Void> {
         val source = TaskCompletionSource<Void>()
         val body = RequestBody.create(FirebaseAuth.getInstance(app).json, JsonObject(mapOf("idToken" to JsonPrimitive(idToken))).toString())
-        val request = Request.Builder()
-            .url(urlFactory.buildUrl("www.googleapis.com/identitytoolkit/v3/relyingparty/deleteAccount"))
-            .post(body)
-            .build()
-        FirebaseAuth.getInstance(app).client.newCall(request).enqueue(object : Callback {
-
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
-            }
+        val request =
+            Request
+                .Builder()
+                .url(urlFactory.buildUrl("www.googleapis.com/identitytoolkit/v3/relyingparty/deleteAccount"))
+                .post(body)
+                .build()
+        FirebaseAuth.getInstance(app).client.newCall(request).enqueue(
+            object : Callback {
+                override fun onFailure(
+                    call: Call,
+                    e: IOException,
+                ) {
+                    source.setException(FirebaseException(e.toString(), e))
+                }
 
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    FirebaseAuth.getInstance(app).signOut()
-                    source.setException(
-                        FirebaseAuth.getInstance(app).createAuthInvalidUserException(
-                            "deleteAccount",
-                            request,
-                            response
+                @Throws(IOException::class)
+                override fun onResponse(
+                    call: Call,
+                    response: Response,
+                ) {
+                    if (!response.isSuccessful) {
+                        FirebaseAuth.getInstance(app).signOut()
+                        source.setException(
+                            FirebaseAuth.getInstance(app).createAuthInvalidUserException(
+                                "deleteAccount",
+                                request,
+                                response,
+                            ),
                         )
-                    )
-                } else {
-                    source.setResult(null)
+                    } else {
+                        source.setResult(null)
+                    }
                 }
-            }
-        })
+            },
+        )
         return source.task
     }
 
@@ -141,104 +158,142 @@ class FirebaseUserImpl internal constructor(
         return source.task
     }
 
-    //TODO implement ActionCodeSettings and pass it to the url
+    // TODO implement ActionCodeSettings and pass it to the url
     override fun verifyBeforeUpdateEmail(
         newEmail: String,
-        actionCodeSettings: ActionCodeSettings?
+        actionCodeSettings: ActionCodeSettings?,
     ): Task<Unit> {
         val source = TaskCompletionSource<Unit>()
-        val body = RequestBody.create(
-            FirebaseAuth.getInstance(app).json,
-            JsonObject(
-                mapOf(
-                    "idToken" to JsonPrimitive(idToken),
-                    "email" to JsonPrimitive(email),
-                    "newEmail" to JsonPrimitive(newEmail),
-                    "requestType" to JsonPrimitive(OobRequestType.VERIFY_AND_CHANGE_EMAIL.name)
-                )
-            ).toString()
-        )
-        val request = Request.Builder()
-            .url("https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=" + app.options.apiKey)
-            .post(body)
-            .build()
-        FirebaseAuth.getInstance(app).client.newCall(request).enqueue(object : Callback {
-
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
-                e.printStackTrace()
-            }
+        val body =
+            RequestBody.create(
+                FirebaseAuth.getInstance(app).json,
+                JsonObject(
+                    mapOf(
+                        "idToken" to JsonPrimitive(idToken),
+                        "email" to JsonPrimitive(email),
+                        "newEmail" to JsonPrimitive(newEmail),
+                        "requestType" to JsonPrimitive(OobRequestType.VERIFY_AND_CHANGE_EMAIL.name),
+                    ),
+                ).toString(),
+            )
+        val request =
+            Request
+                .Builder()
+                .url(urlFactory.buildUrl("identitytoolkit.googleapis.com/v1/accounts:sendOobCode"))
+                .post(body)
+                .build()
+        FirebaseAuth.getInstance(app).client.newCall(request).enqueue(
+            object : Callback {
+                override fun onFailure(
+                    call: Call,
+                    e: IOException,
+                ) {
+                    source.setException(FirebaseException(e.toString(), e))
+                    e.printStackTrace()
+                }
 
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    FirebaseAuth.getInstance(app).signOut()
-                    source.setException(
-                        FirebaseAuth.getInstance(app).createAuthInvalidUserException(
-                            "verifyEmail",
-                            request,
-                            response
+                @Throws(IOException::class)
+                override fun onResponse(
+                    call: Call,
+                    response: Response,
+                ) {
+                    if (!response.isSuccessful) {
+                        FirebaseAuth.getInstance(app).signOut()
+                        source.setException(
+                            FirebaseAuth.getInstance(app).createAuthInvalidUserException(
+                                "verifyEmail",
+                                request,
+                                response,
+                            ),
                         )
-                    )
-                } else {
-                    source.setResult(null)
+                    } else {
+                        source.setResult(null)
+                    }
                 }
-            }
-        })
+            },
+        )
         return source.task
     }
 
     override fun getIdToken(forceRefresh: Boolean) = FirebaseAuth.getInstance(app).getAccessToken(forceRefresh)
-}
 
-class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
+    override fun updateProfile(request: UserProfileChangeRequest): Task<Unit> = FirebaseAuth.getInstance(app).updateProfile(request)
+
+    fun updateProfile(
+        displayName: String?,
+        photoUrl: String?,
+    ): Task<Unit> {
+        val request =
+            UserProfileChangeRequest
+                .Builder()
+                .apply { setDisplayName(displayName) }
+                .apply { setPhotoUri(photoUrl?.let { Uri.parse(it) }) }
+                .build()
+        return FirebaseAuth.getInstance(app).updateProfile(request)
+    }
+}
 
+class FirebaseAuth constructor(
+    val app: FirebaseApp,
+) : InternalAuthProvider {
     internal val json = MediaType.parse("application/json; charset=utf-8")
-    internal val client: OkHttpClient = OkHttpClient.Builder()
-        .connectTimeout(60, TimeUnit.SECONDS)
-        .readTimeout(60, TimeUnit.SECONDS)
-        .writeTimeout(60, TimeUnit.SECONDS)
-        .build()
+    internal val client: OkHttpClient =
+        OkHttpClient
+            .Builder()
+            .connectTimeout(60, TimeUnit.SECONDS)
+            .readTimeout(60, TimeUnit.SECONDS)
+            .writeTimeout(60, TimeUnit.SECONDS)
+            .build()
 
     private fun enqueueAuthPost(
         url: String,
         body: RequestBody,
-        setResult: (responseBody: String) -> FirebaseUserImpl?
+        setResult: (responseBody: String) -> FirebaseUserImpl?,
     ): TaskCompletionSource<AuthResult> {
         val source = TaskCompletionSource<AuthResult>()
-        val request = Request.Builder()
-            .url("$url?key=" + app.options.apiKey)
-            .post(body)
-            .build()
-
-        client.newCall(request).enqueue(object : Callback {
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
-            }
+        val request =
+            Request
+                .Builder()
+                .url(urlFactory.buildUrl(url))
+                .post(body)
+                .build()
+
+        client.newCall(request).enqueue(
+            object : Callback {
+                override fun onFailure(
+                    call: Call,
+                    e: IOException,
+                ) {
+                    source.setException(FirebaseException(e.toString(), e))
+                }
 
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    source.setException(
-                        createAuthInvalidUserException("accounts", request, response)
-                    )
-                } else {
-                    if(response.body()?.use { it.string() }?.also { responseBody ->
-                            user = setResult(responseBody)
-                            source.setResult(AuthResult { user })
-                        } == null) {
+                @Throws(IOException::class)
+                override fun onResponse(
+                    call: Call,
+                    response: Response,
+                ) {
+                    if (!response.isSuccessful) {
                         source.setException(
-                            createAuthInvalidUserException("accounts", request, response)
+                            createAuthInvalidUserException("accounts", request, response),
                         )
+                    } else {
+                        if (response.body()?.use { it.string() }?.also { responseBody ->
+                                user = setResult(responseBody)
+                                source.setResult(AuthResult { user })
+                            } == null
+                        ) {
+                            source.setException(
+                                createAuthInvalidUserException("accounts", request, response),
+                            )
+                        }
                     }
                 }
-            }
-        })
+            },
+        )
         return source
     }
 
     companion object {
-
         @JvmStatic
         fun getInstance(): FirebaseAuth = getInstance(FirebaseApp.getInstance())
 
@@ -257,10 +312,11 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
 
     val FirebaseApp.key get() = "com.google.firebase.auth.FIREBASE_USER${"[$name]".takeUnless { isDefaultApp }.orEmpty()}"
 
-    private var user: FirebaseUserImpl? = FirebasePlatform.firebasePlatform
-        .runCatching { retrieve(app.key)?.let { FirebaseUserImpl(app, jsonParser.parseToJsonElement(it).jsonObject) } }
-        .onFailure { it.printStackTrace() }
-        .getOrNull()
+    private var user: FirebaseUserImpl? =
+        FirebasePlatform.firebasePlatform
+            .runCatching { retrieve(app.key)?.let { FirebaseUserImpl(app, data = jsonParser.parseToJsonElement(it).jsonObject) } }
+            .onFailure { it.printStackTrace() }
+            .getOrNull()
 
         private set(value) {
             if (field != value) {
@@ -295,93 +351,185 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
     private var urlFactory = UrlFactory(app)
 
     fun signInAnonymously(): Task<AuthResult> {
-        val source = enqueueAuthPost(
-            url = "https://identitytoolkit.googleapis.com/v1/accounts:signUp",
-            body = RequestBody.create(json, JsonObject(mapOf("returnSecureToken" to JsonPrimitive(true))).toString()),
-            setResult = { responseBody ->
-                FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject, isAnonymous = true)
-            }
-        )
+        val source =
+            enqueueAuthPost(
+                url = "identitytoolkit.googleapis.com/v1/accounts:signUp",
+                body = RequestBody.create(json, JsonObject(mapOf("returnSecureToken" to JsonPrimitive(true))).toString()),
+                setResult = { responseBody ->
+                    FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject, isAnonymous = true)
+                },
+            )
         return source.task
     }
 
     fun signInWithCustomToken(customToken: String): Task<AuthResult> {
-        val source = enqueueAuthPost(
-            url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken",
-            body = RequestBody.create(
-                json,
-                JsonObject(mapOf("token" to JsonPrimitive(customToken), "returnSecureToken" to JsonPrimitive(true))).toString()
-            ),
-            setResult = { responseBody ->
-                FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
+        val source =
+            enqueueAuthPost(
+                url = "www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken",
+                body =
+                    RequestBody.create(
+                        json,
+                        JsonObject(mapOf("token" to JsonPrimitive(customToken), "returnSecureToken" to JsonPrimitive(true))).toString(),
+                    ),
+                setResult = { responseBody ->
+                    FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
+                },
+            ).task.continueWith {
+                updateByGetAccountInfo()
             }
-        )
-        return source.task
+        return source.result
     }
 
-    fun createUserWithEmailAndPassword(email: String, password: String): Task<AuthResult> {
-        val source = enqueueAuthPost(
-            url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser",
-            body = RequestBody.create(
+    internal fun updateByGetAccountInfo(): Task<AuthResult> {
+        val source = TaskCompletionSource<AuthResult>()
+
+        val body =
+            RequestBody.create(
                 json,
-                JsonObject(
-                    mapOf(
-                        "email" to JsonPrimitive(email),
-                        "password" to JsonPrimitive(password),
-                        "returnSecureToken" to JsonPrimitive(true)
-                    )
-                ).toString()
-            ),
-            setResult = { responseBody ->
-                FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
-            }
+                JsonObject(mapOf("idToken" to JsonPrimitive(user?.idToken))).toString(),
+            )
+        val request =
+            Request
+                .Builder()
+                .url(urlFactory.buildUrl("www.googleapis.com/identitytoolkit/v3/relyingparty/getAccountInfo"))
+                .post(body)
+                .build()
+
+        client.newCall(request).enqueue(
+            object : Callback {
+                override fun onFailure(
+                    call: Call,
+                    e: IOException,
+                ) {
+                    source.setException(FirebaseException(e.toString(), e))
+                }
+
+                @Throws(IOException::class)
+                override fun onResponse(
+                    call: Call,
+                    response: Response,
+                ) {
+                    if (!response.isSuccessful) {
+                        source.setException(
+                            createAuthInvalidUserException("updateWithAccountInfo", request, response),
+                        )
+                    } else {
+                        val newBody =
+                            jsonParser
+                                .parseToJsonElement(
+                                    response.body()?.use { it.string() } ?: "",
+                                ).jsonObject
+
+                        user?.let { prev ->
+                            user =
+                                FirebaseUserImpl(
+                                    app = app,
+                                    isAnonymous = prev.isAnonymous,
+                                    uid = prev.uid,
+                                    idToken = prev.idToken,
+                                    refreshToken = prev.refreshToken,
+                                    expiresIn = prev.expiresIn,
+                                    createdAt = newBody["createdAt"]?.jsonPrimitive?.longOrNull ?: prev.createdAt,
+                                    email = newBody["email"]?.jsonPrimitive?.contentOrNull ?: prev.email,
+                                    photoUrl = newBody["photoUrl"]?.jsonPrimitive?.contentOrNull ?: prev.photoUrl,
+                                    displayName = newBody["displayName"]?.jsonPrimitive?.contentOrNull ?: prev.displayName,
+                                )
+                            source.setResult(AuthResult { user })
+                        }
+                        source.setResult(null)
+                    }
+                }
+            },
         )
         return source.task
     }
 
-    fun signInWithEmailAndPassword(email: String, password: String): Task<AuthResult> {
-        val source = enqueueAuthPost(
-            url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword",
-            body = RequestBody.create(
-                json,
-                JsonObject(
-                    mapOf(
-                        "email" to JsonPrimitive(email),
-                        "password" to JsonPrimitive(password),
-                        "returnSecureToken" to JsonPrimitive(true)
+    fun createUserWithEmailAndPassword(
+        email: String,
+        password: String,
+    ): Task<AuthResult> {
+        val source =
+            enqueueAuthPost(
+                url = "www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser",
+                body =
+                    RequestBody.create(
+                        json,
+                        JsonObject(
+                            mapOf(
+                                "email" to JsonPrimitive(email),
+                                "password" to JsonPrimitive(password),
+                                "returnSecureToken" to JsonPrimitive(true),
+                            ),
+                        ).toString(),
+                    ),
+                setResult = { responseBody ->
+                    FirebaseUserImpl(
+                        app = app,
+                        data = jsonParser.parseToJsonElement(responseBody).jsonObject,
                     )
-                ).toString()
-            ),
-            setResult = { responseBody ->
-                FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
-            }
-        )
+                },
+            )
+        return source.task
+    }
+
+    fun signInWithEmailAndPassword(
+        email: String,
+        password: String,
+    ): Task<AuthResult> {
+        val source =
+            enqueueAuthPost(
+                url = "www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword",
+                body =
+                    RequestBody.create(
+                        json,
+                        JsonObject(
+                            mapOf(
+                                "email" to JsonPrimitive(email),
+                                "password" to JsonPrimitive(password),
+                                "returnSecureToken" to JsonPrimitive(true),
+                            ),
+                        ).toString(),
+                    ),
+                setResult = { responseBody ->
+                    FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
+                },
+            )
         return source.task
     }
 
     internal fun createAuthInvalidUserException(
         action: String,
         request: Request,
-        response: Response
+        response: Response,
     ): FirebaseAuthInvalidUserException {
         val body = response.body()!!.use { it.string() }
         val jsonObject = jsonParser.parseToJsonElement(body).jsonObject
 
         return FirebaseAuthInvalidUserException(
-            jsonObject["error"]?.jsonObject
-                ?.get("message")?.jsonPrimitive
+            jsonObject["error"]
+                ?.jsonObject
+                ?.get("message")
+                ?.jsonPrimitive
                 ?.contentOrNull
                 ?: "UNKNOWN_ERROR",
             "$action API returned an error, " +
-                    "with url [${request.method()}] ${request.url()} ${request.body()} -- " +
-                    "response [${response.code()}] ${response.message()} $body"
+                "with url [${request.method()}] ${request.url()} ${request.body()} -- " +
+                "response [${response.code()}] ${response.message()} $body",
         )
     }
 
     fun signOut() {
         // cancel token refresher
-        client.dispatcher().queuedCalls().find { it.request().tag() == REFRESH_TOKEN_TAG }?.cancel() ?: {
-            client.dispatcher().runningCalls().find { it.request().tag() == REFRESH_TOKEN_TAG }?.cancel()
+        client
+            .dispatcher()
+            .queuedCalls()
+            .find { it.request().tag() == REFRESH_TOKEN_TAG }
+            ?.cancel() ?: {
+            client
+                .dispatcher()
+                .runningCalls()
+                .find { it.request().tag() == REFRESH_TOKEN_TAG }
+                ?.cancel()
         }
         user = null
     }
@@ -401,7 +549,11 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
 
     private var refreshSource = TaskCompletionSource<FirebaseUserImpl>().apply { setException(Exception()) }
 
-    internal fun <T> refreshToken(user: FirebaseUserImpl, source: TaskCompletionSource<T>, map: (user: FirebaseUserImpl) -> T?) {
+    internal fun <T> refreshToken(
+        user: FirebaseUserImpl,
+        source: TaskCompletionSource<T>,
+        map: (user: FirebaseUserImpl) -> T?,
+    ) {
         refreshSource = refreshSource
             .takeUnless { it.task.isComplete }
             ?: enqueueRefreshTokenCall(user)
@@ -411,119 +563,220 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
 
     private fun enqueueRefreshTokenCall(user: FirebaseUserImpl): TaskCompletionSource<FirebaseUserImpl> {
         val source = TaskCompletionSource<FirebaseUserImpl>()
-        val body = RequestBody.create(
-            json,
-            JsonObject(
-                mapOf(
-                    "refresh_token" to JsonPrimitive(user.refreshToken),
-                    "grant_type" to JsonPrimitive("refresh_token")
-                )
-            ).toString()
-        )
-        val request = Request.Builder()
-            .url(urlFactory.buildUrl("securetoken.googleapis.com/v1/token"))
-            .post(body)
-            .tag(REFRESH_TOKEN_TAG)
-            .build()
-
-        client.newCall(request).enqueue(object : Callback {
-
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(e)
-            }
-
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                val responseBody = response.body()?.use { it.string() }
+        val body =
+            RequestBody.create(
+                json,
+                JsonObject(
+                    mapOf(
+                        "refresh_token" to JsonPrimitive(user.refreshToken),
+                        "grant_type" to JsonPrimitive("refresh_token"),
+                    ),
+                ).toString(),
+            )
+        val request =
+            Request
+                .Builder()
+                .url(urlFactory.buildUrl("securetoken.googleapis.com/v1/token"))
+                .post(body)
+                .tag(REFRESH_TOKEN_TAG)
+                .build()
+
+        client.newCall(request).enqueue(
+            object : Callback {
+                override fun onFailure(
+                    call: Call,
+                    e: IOException,
+                ) {
+                    source.setException(e)
+                }
 
-                if (!response.isSuccessful || responseBody == null) {
-                    signOutAndThrowInvalidUserException(responseBody.orEmpty(), "token API returned an error: $body")
-                } else {
-                    jsonParser.parseToJsonElement(responseBody).jsonObject.apply {
-                        val newUser = FirebaseUserImpl(app, this, user.isAnonymous, user.email)
-                        if (newUser.claims["aud"] != app.options.projectId) {
-                            signOutAndThrowInvalidUserException(
-                                newUser.claims.toString(),
-                                "Project ID's do not match ${newUser.claims["aud"]} != ${app.options.projectId}"
-                            )
-                        } else {
-                            this@FirebaseAuth.user = newUser
-                            source.setResult(newUser)
+                @Throws(IOException::class)
+                override fun onResponse(
+                    call: Call,
+                    response: Response,
+                ) {
+                    val responseBody = response.body()?.use { it.string() }
+
+                    if (!response.isSuccessful || responseBody == null) {
+                        signOutAndThrowInvalidUserException(responseBody.orEmpty(), "token API returned an error: $body")
+                    } else {
+                        jsonParser.parseToJsonElement(responseBody).jsonObject.apply {
+                            val newUser = FirebaseUserImpl(app, this, user.isAnonymous, user.email)
+                            if (newUser.claims["aud"] != app.options.projectId) {
+                                signOutAndThrowInvalidUserException(
+                                    newUser.claims.toString(),
+                                    "Project ID's do not match ${newUser.claims["aud"]} != ${app.options.projectId}",
+                                )
+                            } else {
+                                this@FirebaseAuth.user = newUser
+                                source.setResult(newUser)
+                            }
                         }
                     }
                 }
-            }
 
-            private fun signOutAndThrowInvalidUserException(body: String, message: String) {
-                signOut()
-                source.setException(FirebaseAuthInvalidUserException(body, message))
-            }
-        })
+                private fun signOutAndThrowInvalidUserException(
+                    body: String,
+                    message: String,
+                ) {
+                    signOut()
+                    source.setException(FirebaseAuthInvalidUserException(body, message))
+                }
+            },
+        )
         return source
     }
 
     internal fun updateEmail(email: String): Task<Unit> {
         val source = TaskCompletionSource<Unit>()
 
-        val body = RequestBody.create(
-            json,
-            JsonObject(
-                mapOf(
-                    "idToken" to JsonPrimitive(user?.idToken),
-                    "email" to JsonPrimitive(email),
-                    "returnSecureToken" to JsonPrimitive(true)
-                )
-            ).toString()
+        val body =
+            RequestBody.create(
+                json,
+                JsonObject(
+                    mapOf(
+                        "idToken" to JsonPrimitive(user?.idToken),
+                        "email" to JsonPrimitive(email),
+                        "returnSecureToken" to JsonPrimitive(true),
+                    ),
+                ).toString(),
+            )
+        val request =
+            Request
+                .Builder()
+                .url(urlFactory.buildUrl("identitytoolkit.googleapis.com/v1/accounts:update"))
+                .post(body)
+                .build()
+
+        client.newCall(request).enqueue(
+            object : Callback {
+                override fun onFailure(
+                    call: Call,
+                    e: IOException,
+                ) {
+                    source.setException(FirebaseException(e.toString(), e))
+                }
+
+                @Throws(IOException::class)
+                override fun onResponse(
+                    call: Call,
+                    response: Response,
+                ) {
+                    if (!response.isSuccessful) {
+                        signOut()
+                        source.setException(
+                            createAuthInvalidUserException(
+                                "updateEmail",
+                                request,
+                                response,
+                            ),
+                        )
+                    } else {
+                        val newBody =
+                            jsonParser
+                                .parseToJsonElement(
+                                    response.body()?.use { it.string() } ?: "",
+                                ).jsonObject
+
+                        user?.let { prev ->
+                            user =
+                                FirebaseUserImpl(
+                                    app = app,
+                                    isAnonymous = prev.isAnonymous,
+                                    uid = prev.uid,
+                                    idToken = newBody["idToken"]?.jsonPrimitive?.contentOrNull ?: prev.idToken,
+                                    refreshToken = newBody["refreshToken"]?.jsonPrimitive?.contentOrNull ?: prev.refreshToken,
+                                    expiresIn = newBody["expiresIn"]?.jsonPrimitive?.intOrNull ?: prev.expiresIn,
+                                    createdAt = prev.createdAt,
+                                    email = newBody["newEmail"]?.jsonPrimitive?.contentOrNull ?: prev.email,
+                                    photoUrl = newBody["photoUrl"]?.jsonPrimitive?.contentOrNull ?: prev.photoUrl,
+                                    displayName = newBody["displayName"]?.jsonPrimitive?.contentOrNull ?: prev.displayName,
+                                )
+                        }
+                        source.setResult(null)
+                    }
+                }
+            },
         )
-        val request = Request.Builder()
-            .url("https://identitytoolkit.googleapis.com/v1/accounts:update?key=" + app.options.apiKey)
-            .post(body)
-            .build()
+        return source.task
+    }
 
-        client.newCall(request).enqueue(object : Callback {
+    internal fun updateProfile(request: UserProfileChangeRequest): Task<Unit> {
+        val source = TaskCompletionSource<Unit>()
 
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
-            }
+        val body =
+            RequestBody.create(
+                json,
+                JsonObject(
+                    mapOf(
+                        "idToken" to JsonPrimitive(user?.idToken),
+                        "displayName" to JsonPrimitive(request.displayName),
+                        "photoUrl" to JsonPrimitive(request.photoUrl),
+                        "returnSecureToken" to JsonPrimitive(true),
+                    ),
+                ).toString(),
+            )
+        val req =
+            Request
+                .Builder()
+                .url(urlFactory.buildUrl("identitytoolkit.googleapis.com/v1/accounts:update"))
+                .post(body)
+                .build()
+
+        client.newCall(req).enqueue(
+            object : Callback {
+                override fun onFailure(
+                    call: Call,
+                    e: IOException,
+                ) {
+                    source.setException(FirebaseException(e.toString(), e))
+                }
 
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    signOut()
-                    source.setException(
-                        createAuthInvalidUserException(
-                            "updateEmail",
-                            request,
-                            response
-                        )
-                    )
-                } else {
-                    val newBody = jsonParser.parseToJsonElement(
-                        response.body()?.use { it.string() } ?: ""
-                    ).jsonObject
-
-                    user?.let { prev ->
-                        user = FirebaseUserImpl(
-                            app = app,
-                            isAnonymous = prev.isAnonymous,
-                            uid = prev.uid,
-                            idToken = newBody["idToken"]?.jsonPrimitive?.contentOrNull ?: prev.idToken,
-                            refreshToken = newBody["refreshToken"]?.jsonPrimitive?.contentOrNull ?: prev.refreshToken,
-                            expiresIn = newBody["expiresIn"]?.jsonPrimitive?.intOrNull ?: prev.expiresIn,
-                            createdAt = prev.createdAt,
-                            email = newBody["newEmail"]?.jsonPrimitive?.contentOrNull ?: prev.email
+                @Throws(IOException::class)
+                override fun onResponse(
+                    call: Call,
+                    response: Response,
+                ) {
+                    if (!response.isSuccessful) {
+                        signOut()
+                        source.setException(
+                            createAuthInvalidUserException(
+                                "updateProfile",
+                                req,
+                                response,
+                            ),
                         )
+                    } else {
+                        val newBody =
+                            jsonParser
+                                .parseToJsonElement(
+                                    response.body()?.use { it.string() } ?: "",
+                                ).jsonObject
+
+                        user?.let { prev ->
+                            user =
+                                FirebaseUserImpl(
+                                    app = app,
+                                    isAnonymous = prev.isAnonymous,
+                                    uid = prev.uid,
+                                    idToken = newBody["idToken"]?.jsonPrimitive?.contentOrNull ?: prev.idToken,
+                                    refreshToken = newBody["refreshToken"]?.jsonPrimitive?.contentOrNull ?: prev.refreshToken,
+                                    expiresIn = newBody["expiresIn"]?.jsonPrimitive?.intOrNull ?: prev.expiresIn,
+                                    createdAt = prev.createdAt,
+                                    email = newBody["newEmail"]?.jsonPrimitive?.contentOrNull ?: prev.email,
+                                    photoUrl = newBody["photoUrl"]?.jsonPrimitive?.contentOrNull ?: prev.photoUrl,
+                                    displayName = newBody["displayName"]?.jsonPrimitive?.contentOrNull ?: prev.displayName,
+                                )
+                        }
+                        source.setResult(null)
                     }
-                    source.setResult(null)
                 }
-            }
-        })
+            },
+        )
         return source.task
     }
 
-    override fun getUid(): String? {
-        return user?.uid
-    }
+    override fun getUid(): String? = user?.uid
 
     override fun addIdTokenListener(listener: com.google.firebase.auth.internal.IdTokenListener) {
         internalIdTokenListeners.addIfAbsent(listener)
@@ -570,21 +823,46 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
         idTokenListeners.remove(listener)
     }
 
-    fun sendPasswordResetEmail(email: String, settings: ActionCodeSettings?): Task<Unit> = TODO()
+    fun sendPasswordResetEmail(
+        email: String,
+        settings: ActionCodeSettings?,
+    ): Task<Unit> = TODO()
+
     fun checkActionCode(code: String): Task<ActionCodeResult> = TODO()
-    fun confirmPasswordReset(code: String, newPassword: String): Task<Unit> = TODO()
+
+    fun confirmPasswordReset(
+        code: String,
+        newPassword: String,
+    ): Task<Unit> = TODO()
+
     fun fetchSignInMethodsForEmail(email: String): Task<SignInMethodQueryResult> = TODO()
-    fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings): Task<Unit> = TODO()
+
+    fun sendSignInLinkToEmail(
+        email: String,
+        actionCodeSettings: ActionCodeSettings,
+    ): Task<Unit> = TODO()
+
     fun verifyPasswordResetCode(code: String): Task<String> = TODO()
+
     fun updateCurrentUser(user: FirebaseUser): Task<Unit> = TODO()
+
     fun applyActionCode(code: String): Task<Unit> = TODO()
+
     val languageCode: String get() = TODO()
+
     fun isSignInWithEmailLink(link: String): Boolean = TODO()
-    fun signInWithEmailLink(email: String, link: String): Task<AuthResult> = TODO()
+
+    fun signInWithEmailLink(
+        email: String,
+        link: String,
+    ): Task<AuthResult> = TODO()
 
     fun setLanguageCode(value: String): Nothing = TODO()
 
-    fun useEmulator(host: String, port: Int) {
+    fun useEmulator(
+        host: String,
+        port: Int,
+    ) {
         urlFactory = UrlFactory(app, "http://$host:$port/")
     }
 }
diff --git a/src/main/java/com/google/firebase/auth/FirebaseUser.kt b/src/main/java/com/google/firebase/auth/FirebaseUser.kt
index 50e4e52..0932518 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseUser.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseUser.kt
@@ -5,28 +5,45 @@ import com.google.android.gms.tasks.Task
 abstract class FirebaseUser {
     abstract val uid: String
     abstract val email: String?
+    abstract val photoUrl: String?
+    abstract val displayName: String?
     abstract val isAnonymous: Boolean
+
     abstract fun delete(): Task<Void>
+
     abstract fun reload(): Task<Void>
-    abstract fun verifyBeforeUpdateEmail(newEmail: String, actionCodeSettings: ActionCodeSettings?): Task<Unit>
+
+    abstract fun verifyBeforeUpdateEmail(
+        newEmail: String,
+        actionCodeSettings: ActionCodeSettings?,
+    ): Task<Unit>
+
     abstract fun updateEmail(email: String): Task<Unit>
 
-    val displayName: String get() = TODO()
+    abstract fun getIdToken(forceRefresh: Boolean): Task<GetTokenResult>
+
+    abstract fun updateProfile(request: UserProfileChangeRequest): Task<Unit>
+
     val phoneNumber: String get() = TODO()
-    val photoUrl: String? get() = TODO()
     val isEmailVerified: Boolean get() = TODO()
     val metadata: FirebaseUserMetadata get() = TODO()
     val multiFactor: MultiFactor get() = TODO()
     val providerData: List<UserInfo> get() = TODO()
     val providerId: String get() = TODO()
-    abstract fun getIdToken(forceRefresh: Boolean): Task<GetTokenResult>
+
     fun linkWithCredential(credential: AuthCredential): Task<AuthResult> = TODO()
+
     fun sendEmailVerification(): Task<Unit> = TODO()
+
     fun sendEmailVerification(actionCodeSettings: ActionCodeSettings): Task<Unit> = TODO()
+
     fun unlink(provider: String): Task<AuthResult> = TODO()
+
     fun updatePassword(password: String): Task<Unit> = TODO()
+
     fun updatePhoneNumber(credential: AuthCredential): Task<Unit> = TODO()
-    fun updateProfile(request: UserProfileChangeRequest): Task<Unit> = TODO()
+
     fun reauthenticate(credential: AuthCredential): Task<Unit> = TODO()
+
     fun reauthenticateAndRetrieveData(credential: AuthCredential): Task<AuthResult> = TODO()
 }
diff --git a/src/main/java/com/google/firebase/auth/OobRequestType.kt b/src/main/java/com/google/firebase/auth/OobRequestType.kt
index f0c31c3..51a77e2 100644
--- a/src/main/java/com/google/firebase/auth/OobRequestType.kt
+++ b/src/main/java/com/google/firebase/auth/OobRequestType.kt
@@ -5,4 +5,4 @@ internal enum class OobRequestType {
     EMAIL_SIGNIN,
     VERIFY_EMAIL,
     VERIFY_AND_CHANGE_EMAIL
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.java b/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.java
deleted file mode 100644
index 437f98d..0000000
--- a/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.google.firebase.auth;
-
-import android.net.Uri;
-import kotlin.NotImplementedError;
-
-public class UserProfileChangeRequest {
-    public static class Builder {
-        public Builder setDisplayName(String name) {
-            throw new NotImplementedError();
-        }
-        public Builder setPhotoUri(Uri uri) {
-            throw new NotImplementedError();
-        }
-        public UserProfileChangeRequest build() {
-            throw new NotImplementedError();
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.kt b/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.kt
new file mode 100644
index 0000000..c60bb83
--- /dev/null
+++ b/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.kt
@@ -0,0 +1,47 @@
+package com.google.firebase.auth
+
+import android.net.Uri
+import android.os.Parcel
+import android.os.Parcelable
+
+class UserProfileChangeRequest private constructor(
+    internal val displayName: String?,
+    internal val photoUrl: String?,
+) : Parcelable {
+    override fun describeContents(): Int = displayName.hashCode() + photoUrl.hashCode()
+
+    override fun writeToParcel(
+        dest: Parcel,
+        flags: Int,
+    ) {
+        dest.writeString(displayName)
+        dest.writeString(photoUrl)
+    }
+
+    internal companion object CREATOR : Parcelable.Creator<UserProfileChangeRequest> {
+        override fun createFromParcel(parcel: Parcel): UserProfileChangeRequest {
+            val displayName = parcel.readString()
+            val photoUri = parcel.readString()
+            return UserProfileChangeRequest(displayName, photoUri)
+        }
+
+        override fun newArray(size: Int): Array<UserProfileChangeRequest?> = arrayOfNulls(size)
+    }
+
+    class Builder {
+        private var displayName: String? = null
+        private var photoUri: Uri? = null
+
+        fun setDisplayName(name: String?): Builder {
+            this.displayName = name
+            return this
+        }
+
+        fun setPhotoUri(uri: Uri?): Builder {
+            this.photoUri = uri
+            return this
+        }
+
+        fun build(): UserProfileChangeRequest = UserProfileChangeRequest(displayName, photoUri?.toString())
+    }
+}
diff --git a/src/test/kotlin/AppTest.kt b/src/test/kotlin/AppTest.kt
index d017e38..2e5697f 100644
--- a/src/test/kotlin/AppTest.kt
+++ b/src/test/kotlin/AppTest.kt
@@ -8,20 +8,34 @@ import org.junit.Test
 class AppTest : FirebaseTest() {
     @Test
     fun testInitialize() {
-        FirebasePlatform.initializeFirebasePlatform(object : FirebasePlatform() {
-            val storage = mutableMapOf<String, String>()
-            override fun store(key: String, value: String) = storage.set(key, value)
-            override fun retrieve(key: String) = storage[key]
-            override fun clear(key: String) { storage.remove(key) }
-            override fun log(msg: String) = println(msg)
-        })
-        val options = FirebaseOptions.Builder()
-            .setProjectId("my-firebase-project")
-            .setApplicationId("1:27992087142:android:ce3b6448250083d1")
-            .setApiKey("AIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
-            // setDatabaseURL(...)
-            // setStorageBucket(...)
-            .build()
+        FirebasePlatform.initializeFirebasePlatform(
+            object : FirebasePlatform() {
+                val storage = mutableMapOf<String, String>()
+
+                override fun store(
+                    key: String,
+                    value: String,
+                ) = storage.set(key, value)
+
+                override fun retrieve(key: String) = storage[key]
+
+                override fun clear(key: String) {
+                    storage.remove(key)
+                }
+
+                override fun log(msg: String) = println(msg)
+            },
+        )
+        val options =
+            FirebaseOptions
+                .Builder()
+                .setProjectId("fir-java-sdk")
+                .setApplicationId("1:341458593155:web:bf8e1aa37efe01f32d42b6")
+                .setApiKey("AIzaSyCvVHjTJHyeStnzIE7J9LLtHqWk6reGM08")
+                .setDatabaseUrl("https://fir-java-sdk-default-rtdb.firebaseio.com")
+                .setStorageBucket("fir-java-sdk.appspot.com")
+                .setGcmSenderId("341458593155")
+                .build()
         val app = Firebase.initialize(Application(), options)
     }
 }
diff --git a/src/test/kotlin/AuthTest.kt b/src/test/kotlin/AuthTest.kt
index 7bdb90c..080c784 100644
--- a/src/test/kotlin/AuthTest.kt
+++ b/src/test/kotlin/AuthTest.kt
@@ -1,47 +1,97 @@
-import com.google.firebase.auth.FirebaseAuth
+import android.net.Uri
 import com.google.firebase.auth.FirebaseAuthInvalidUserException
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.tasks.await
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
 import org.junit.Assert.assertThrows
+import org.junit.Before
 import org.junit.Test
+import java.util.UUID
 
 class AuthTest : FirebaseTest() {
-    private fun createAuth(): FirebaseAuth {
-        return FirebaseAuth(app).apply {
+    private val email = "email${UUID.randomUUID()}@example.com"
+
+    @Before
+    fun initialize() {
+        auth.apply {
             useEmulator("localhost", 9099)
         }
     }
 
     @Test
-    fun `should authenticate via anonymous auth`() = runTest {
-        val auth = createAuth()
+    fun `should authenticate via anonymous auth`() =
+        runTest {
+            auth.signInAnonymously().await()
 
-        auth.signInAnonymously().await()
+            assertEquals(true, auth.currentUser?.isAnonymous)
+        }
 
-        assertEquals(true, auth.currentUser?.isAnonymous)
-    }
+    @Test
+    fun `should create user via email and password`() =
+        runTest {
+            val createResult = auth.createUserWithEmailAndPassword(email, "test123").await()
+            assertNotEquals(null, createResult.user?.uid)
+            assertEquals(null, createResult.user?.displayName)
+            // assertEquals(null, createResult.user?.phoneNumber)
+            assertEquals(false, createResult.user?.isAnonymous)
+            assertEquals(email, createResult.user?.email)
+            assertNotEquals("", createResult.user!!.email)
+
+            val signInResult = auth.signInWithEmailAndPassword(email, "test123").await()
+            assertEquals(createResult.user?.uid, signInResult.user?.uid)
+        }
 
     @Test
-    fun `should authenticate via email and password`() = runTest {
-        val auth = createAuth()
+    fun `should authenticate via email and password`() =
+        runTest {
+            auth.createUserWithEmailAndPassword(email, "test123").await()
 
-        auth.signInWithEmailAndPassword("email@example.com", "securepassword").await()
+            auth.signInWithEmailAndPassword(email, "test123").await()
 
-        assertEquals(false, auth.currentUser?.isAnonymous)
-    }
+            assertEquals(false, auth.currentUser?.isAnonymous)
+        }
 
     @Test
-    fun `should throw exception on invalid password`() {
-        val auth = createAuth()
+    fun `should update displayName and photoUrl`() =
+        runTest {
+            auth
+                .createUserWithEmailAndPassword(email, "test123")
+                .await()
+                .user
+            auth.currentUser
+                ?.updateProfile(
+                    com.google.firebase.auth.UserProfileChangeRequest
+                        .Builder()
+                        .setDisplayName("testDisplayName")
+                        .setPhotoUri(Uri.parse("https://picsum.photos/100"))
+                        .build(),
+                )?.await()
+            assertEquals("testDisplayName", auth.currentUser?.displayName)
+            assertEquals("https://picsum.photos/100", auth.currentUser?.photoUrl)
+        }
 
-        val exception = assertThrows(FirebaseAuthInvalidUserException::class.java) {
-            runBlocking {
-                auth.signInWithEmailAndPassword("email@example.com", "wrongpassword").await()
-            }
+    @Test
+    fun `should sign in anonymously`() =
+        runTest {
+            val signInResult = auth.signInAnonymously().await()
+            assertNotEquals("", signInResult.user!!.email)
+            assertEquals(true, signInResult.user?.isAnonymous)
         }
 
-        assertEquals("INVALID_PASSWORD", exception.errorCode)
-    }
+    @Test
+    fun `should throw exception on invalid password`() =
+        runTest {
+            auth.createUserWithEmailAndPassword(email, "test123").await()
+
+            val exception =
+                assertThrows(FirebaseAuthInvalidUserException::class.java) {
+                    runBlocking {
+                        auth.signInWithEmailAndPassword(email, "wrongpassword").await()
+                    }
+                }
+
+            assertEquals("INVALID_PASSWORD", exception.errorCode)
+        }
 }
diff --git a/src/test/kotlin/FirebaseAuthTest.kt b/src/test/kotlin/FirebaseAuthTest.kt
index 029cd11..b2000f2 100644
--- a/src/test/kotlin/FirebaseAuthTest.kt
+++ b/src/test/kotlin/FirebaseAuthTest.kt
@@ -1,71 +1,57 @@
 
-import android.app.Application
-import com.google.firebase.Firebase
-import com.google.firebase.FirebaseOptions
-import com.google.firebase.FirebasePlatform
-import com.google.firebase.auth.FirebaseAuth
-import com.google.firebase.initialize
+import android.net.Uri
+import com.google.firebase.auth.FirebaseUser
 import kotlinx.coroutines.tasks.await
 import kotlinx.coroutines.test.runTest
-import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
-import org.junit.Before
 import org.junit.Test
-import java.io.File
 import java.util.UUID
 
 internal class FirebaseAuthTest : FirebaseTest() {
-
-    private lateinit var auth: FirebaseAuth
-
-    @Before
-    fun initialize() {
-        FirebasePlatform.initializeFirebasePlatform(object : FirebasePlatform() {
-            val storage = mutableMapOf<String, String>()
-            override fun store(key: String, value: String) = storage.set(key, value)
-            override fun retrieve(key: String) = storage[key]
-            override fun clear(key: String) { storage.remove(key) }
-            override fun log(msg: String) = println(msg)
-            override fun getDatabasePath(name: String) = File("./build/$name")
-        })
-        val options = FirebaseOptions.Builder()
-            .setProjectId("fir-java-sdk")
-            .setApplicationId("1:341458593155:web:bf8e1aa37efe01f32d42b6")
-            .setApiKey("AIzaSyCvVHjTJHyeStnzIE7J9LLtHqWk6reGM08")
-            .setDatabaseUrl("https://fir-java-sdk-default-rtdb.firebaseio.com")
-            .setStorageBucket("fir-java-sdk.appspot.com")
-            .setGcmSenderId("341458593155")
-            .build()
-
-        val firebaseApp = Firebase.initialize(Application(), options)
-        auth = FirebaseAuth.getInstance(app = firebaseApp)
-    }
-
-    @After
-    fun clear() {
-        auth.currentUser?.delete()
-    }
-
     @Test
-    fun testCreateUserWithEmailAndPassword() = runTest {
-        val email = "test+${UUID.randomUUID()}@test.com"
-        val createResult = auth.createUserWithEmailAndPassword(email, "test123").await()
-        assertNotEquals(null, createResult.user?.uid)
-        // assertEquals(null, createResult.user?.displayName)
-        // assertEquals(null, createResult.user?.phoneNumber)
-        assertEquals(false, createResult.user?.isAnonymous)
-        assertEquals(email, createResult.user?.email)
-        assertNotEquals("", createResult.user!!.email)
+    fun testCreateUserWithEmailAndPassword() =
+        runTest {
+            val email = "test+${UUID.randomUUID()}@test.com"
+            val createResult = auth.createUserWithEmailAndPassword(email, "test123").await()
+            assertNotEquals(null, createResult.user?.uid)
+            // assertEquals(null, createResult.user?.displayName)
+            // assertEquals(null, createResult.user?.phoneNumber)
+            assertEquals(false, createResult.user?.isAnonymous)
+            assertEquals(email, createResult.user?.email)
+            assertNotEquals("", createResult.user!!.email)
+
+            val signInResult = auth.signInWithEmailAndPassword(email, "test123").await()
+            assertEquals(createResult.user?.uid, signInResult.user?.uid)
+        }
 
-        val signInResult = auth.signInWithEmailAndPassword(email, "test123").await()
-        assertEquals(createResult.user?.uid, signInResult.user?.uid)
-    }
+    @Test
+    fun testUpdateProfile() =
+        runTest {
+            val user = createUser()
+            user
+                ?.updateProfile(
+                    com.google.firebase.auth.UserProfileChangeRequest
+                        .Builder()
+                        .setDisplayName("testDisplayName")
+                        .setPhotoUri(Uri.parse("https://picsum.photos/100"))
+                        .build(),
+                )?.await()
+            assertEquals("testDisplayName", auth.currentUser?.displayName)
+            assertEquals("https://picsum.photos/100", auth.currentUser?.photoUrl)
+        }
 
     @Test
-    fun testSignInAnonymously() = runTest {
-        val signInResult = auth.signInAnonymously().await()
-        assertNotEquals("", signInResult.user!!.email)
-        assertEquals(true, signInResult.user?.isAnonymous)
-    }
+    fun testSignInAnonymously() =
+        runTest {
+            val signInResult = auth.signInAnonymously().await()
+            assertNotEquals("", signInResult.user!!.email)
+            assertEquals(true, signInResult.user?.isAnonymous)
+        }
+
+    private suspend fun createUser(email: String = "test+${UUID.randomUUID()}@test.com"): FirebaseUser? =
+        auth
+            .createUserWithEmailAndPassword(email, "test123")
+            .await()
+            .user
 }
diff --git a/src/test/kotlin/FirebaseTest.kt b/src/test/kotlin/FirebaseTest.kt
index 77aa858..a714f8c 100644
--- a/src/test/kotlin/FirebaseTest.kt
+++ b/src/test/kotlin/FirebaseTest.kt
@@ -3,31 +3,72 @@ import com.google.firebase.Firebase
 import com.google.firebase.FirebaseApp
 import com.google.firebase.FirebaseOptions
 import com.google.firebase.FirebasePlatform
+import com.google.firebase.auth.FirebaseAuth
 import com.google.firebase.initialize
+import com.google.firebase.ktx.initialize
+import org.junit.After
 import org.junit.Before
 import java.io.File
 
 abstract class FirebaseTest {
+    protected lateinit var auth: FirebaseAuth
+
     protected val app: FirebaseApp get() {
-        val options = FirebaseOptions.Builder()
-            .setProjectId("my-firebase-project")
-            .setApplicationId("1:27992087142:android:ce3b6448250083d1")
-            .setApiKey("AIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
-            .build()
+        val options =
+            FirebaseOptions
+                .Builder()
+                .setProjectId("fir-java-sdk")
+                .setApplicationId("1:341458593155:web:bf8e1aa37efe01f32d42b6")
+                .setApiKey("AIzaSyCvVHjTJHyeStnzIE7J9LLtHqWk6reGM08")
+                .setDatabaseUrl("https://fir-java-sdk-default-rtdb.firebaseio.com")
+                .setStorageBucket("fir-java-sdk.appspot.com")
+                .setGcmSenderId("341458593155")
+                .build()
 
         return Firebase.initialize(Application(), options)
     }
 
     @Before
     fun beforeEach() {
-        FirebasePlatform.initializeFirebasePlatform(object : FirebasePlatform() {
-            val storage = mutableMapOf<String, String>()
-            override fun store(key: String, value: String) = storage.set(key, value)
-            override fun retrieve(key: String) = storage[key]
-            override fun clear(key: String) { storage.remove(key) }
-            override fun log(msg: String) = println(msg)
-            override fun getDatabasePath(name: String) = File("./build/$name")
-        })
+        FirebasePlatform.initializeFirebasePlatform(
+            object : FirebasePlatform() {
+                val storage = mutableMapOf<String, String>()
+
+                override fun store(
+                    key: String,
+                    value: String,
+                ) = storage.set(key, value)
+
+                override fun retrieve(key: String) = storage[key]
+
+                override fun clear(key: String) {
+                    storage.remove(key)
+                }
+
+                override fun log(msg: String) = println(msg)
+
+                override fun getDatabasePath(name: String) = File("./build/$name")
+            },
+        )
+        val options =
+            FirebaseOptions
+                .Builder()
+                .setProjectId("fir-java-sdk")
+                .setApplicationId("1:341458593155:web:bf8e1aa37efe01f32d42b6")
+                .setApiKey("AIzaSyCvVHjTJHyeStnzIE7J9LLtHqWk6reGM08")
+                .setDatabaseUrl("https://fir-java-sdk-default-rtdb.firebaseio.com")
+                .setStorageBucket("fir-java-sdk.appspot.com")
+                .setGcmSenderId("341458593155")
+                .build()
+
+        val firebaseApp = Firebase.initialize(Application(), options)
+        auth = FirebaseAuth.getInstance(app = firebaseApp)
+
         FirebaseApp.clearInstancesForTest()
     }
+
+    @After
+    fun clear() {
+        auth.currentUser?.delete()
+    }
 }

From 040414cbcf34a20b790c2110bfb78f82fc831836 Mon Sep 17 00:00:00 2001
From: Jakub Kostka <jacob.cube.mexx@gmail.com>
Date: Thu, 7 Nov 2024 16:38:29 +0100
Subject: [PATCH 10/23] refactor

---
 .../java/com/google/firebase/auth/FirebaseAuth.kt |  4 ++--
 src/test/kotlin/AuthTest.kt                       | 15 +++++++++++++++
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index 5492c67..484afc6 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -374,10 +374,10 @@ class FirebaseAuth constructor(
                 setResult = { responseBody ->
                     FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
                 },
-            ).task.continueWith {
+            ).task.addOnSuccessListener {
                 updateByGetAccountInfo()
             }
-        return source.result
+        return source
     }
 
     internal fun updateByGetAccountInfo(): Task<AuthResult> {
diff --git a/src/test/kotlin/AuthTest.kt b/src/test/kotlin/AuthTest.kt
index 080c784..d603d5f 100644
--- a/src/test/kotlin/AuthTest.kt
+++ b/src/test/kotlin/AuthTest.kt
@@ -53,6 +53,21 @@ class AuthTest : FirebaseTest() {
             assertEquals(false, auth.currentUser?.isAnonymous)
         }
 
+    /*@Test
+    fun `should authenticate via custom token`() =
+        runTest {
+            val user = auth.createUserWithEmailAndPassword(email, "test123").await()
+            auth
+                .signInWithCustomToken(
+                    user.user
+                        .getIdToken(false)
+                        .await()
+                        .token ?: "",
+                ).await()
+
+            assertEquals(false, auth.currentUser?.isAnonymous)
+        }*/
+
     @Test
     fun `should update displayName and photoUrl`() =
         runTest {

From 607dad8bb4721ee84c2fc65c42535ac8d36eea3f Mon Sep 17 00:00:00 2001
From: Nicholas Bransby-Williams <nbransby@gmail.com>
Date: Mon, 16 Sep 2024 08:15:13 +0100
Subject: [PATCH 11/23] Update README.md

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 432d6d7..27cc516 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-<h1 align="left">Firebase Java SDK <img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/gitliveapp/firebase-java-sdk?style=flat-square"> <a href="https://git.live"><img src="https://img.shields.io/badge/collaborate-on%20gitlive-blueviolet?style=flat-square"></a></h1>
+<h1 align="left">Firebase Java SDK <img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/gitliveapp/firebase-java-sdk?style=flat-square"> <a href="https://git.live"><img src="https://img.shields.io/endpoint?style=flat-square&url=https%3A%2F%2Fteamhub-dev.web.app%2Fbadge%3Forg%3DGitLiveApp%26repo%3Dfirebase-java-sdk"></a></h1>
 <img align="left" width="75px" src="https://avatars2.githubusercontent.com/u/42865805?s=200&v=4"> 
   <b>Built and maintained with 🧡 by <a href="https://git.live">GitLive</a></b><br/>
   <i>Development teams merge faster with GitLive</i><br/>

From bce0cd2d3d88559c593ce58a9a538f2914b15ea9 Mon Sep 17 00:00:00 2001
From: Jakub Kostka <jacob.cube.mexx@gmail.com>
Date: Mon, 21 Oct 2024 11:45:12 +0200
Subject: [PATCH 12/23] correct mock Firebase project implementation

---
 src/test/kotlin/FirebaseAuthTest.kt | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/test/kotlin/FirebaseAuthTest.kt b/src/test/kotlin/FirebaseAuthTest.kt
index 6211481..ccbaff3 100644
--- a/src/test/kotlin/FirebaseAuthTest.kt
+++ b/src/test/kotlin/FirebaseAuthTest.kt
@@ -28,12 +28,14 @@ internal class FirebaseAuthTest: FirebaseTest() {
             override fun getDatabasePath(name: String) = File("./build/$name")
         })
         val options = FirebaseOptions.Builder()
-            .setProjectId("my-firebase-project")
-            .setApplicationId("1:27992087142:android:ce3b6448250083d1")
-            .setApiKey("AIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
-            // setDatabaseURL(...)
-            // setStorageBucket(...)
+            .setProjectId("fir-java-sdk")
+            .setApplicationId("1:341458593155:web:bf8e1aa37efe01f32d42b6")
+            .setApiKey("AIzaSyCvVHjTJHyeStnzIE7J9LLtHqWk6reGM08")
+            .setDatabaseUrl("https://fir-java-sdk-default-rtdb.firebaseio.com")
+            .setStorageBucket("fir-java-sdk.appspot.com")
+            .setGcmSenderId("341458593155")
             .build()
+
         val firebaseApp = Firebase.initialize(Application(), options)
         auth = FirebaseAuth.getInstance(app = firebaseApp)
     }

From ec0767608aea92962d9d0c04a6fe37a3675469fd Mon Sep 17 00:00:00 2001
From: nbransby <nbransby@gmail.com>
Date: Sat, 5 Oct 2024 13:28:47 +0800
Subject: [PATCH 13/23] ktlintFormat

---
 .../java/com/google/firebase/auth/FirebaseAuth.kt  | 14 ++++----------
 src/test/kotlin/FirebaseAuthTest.kt                |  8 ++++----
 2 files changed, 8 insertions(+), 14 deletions(-)

diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index 95bd2e7..152963e 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -29,15 +29,9 @@ import kotlinx.serialization.json.intOrNull
 import kotlinx.serialization.json.jsonObject
 import kotlinx.serialization.json.jsonPrimitive
 import kotlinx.serialization.json.longOrNull
-import okhttp3.Call
-import okhttp3.Callback
-import okhttp3.MediaType
-import okhttp3.OkHttpClient
-import okhttp3.Request
-import okhttp3.RequestBody
-import okhttp3.Response
+import okhttp3.*
 import java.io.IOException
-import java.util.Base64
+import java.util.*
 import java.util.concurrent.CopyOnWriteArrayList
 import java.util.concurrent.TimeUnit
 
@@ -60,8 +54,8 @@ class FirebaseUserImpl private constructor(
         app: FirebaseApp,
         data: JsonObject,
         isAnonymous: Boolean = data["isAnonymous"]?.jsonPrimitive?.booleanOrNull ?: false,
-        email: String = data.getOrElse("email"){ null }?.jsonPrimitive?.contentOrNull ?: ""
-    ): this(
+        email: String = data.getOrElse("email") { null }?.jsonPrimitive?.contentOrNull ?: ""
+    ) : this(
         app = app,
         isAnonymous = isAnonymous,
         uid = data["uid"]?.jsonPrimitive?.contentOrNull ?: data["user_id"]?.jsonPrimitive?.contentOrNull ?: data["localId"]?.jsonPrimitive?.contentOrNull ?: "",
diff --git a/src/test/kotlin/FirebaseAuthTest.kt b/src/test/kotlin/FirebaseAuthTest.kt
index ccbaff3..2925db4 100644
--- a/src/test/kotlin/FirebaseAuthTest.kt
+++ b/src/test/kotlin/FirebaseAuthTest.kt
@@ -13,9 +13,9 @@ import org.junit.Test
 import java.io.File
 import kotlin.random.Random
 
-internal class FirebaseAuthTest: FirebaseTest() {
+internal class FirebaseAuthTest : FirebaseTest() {
 
-    private lateinit var auth : FirebaseAuth
+    private lateinit var auth: FirebaseAuth
 
     @Before
     fun initialize() {
@@ -48,8 +48,8 @@ internal class FirebaseAuthTest: FirebaseTest() {
             "test123"
         ).await()
         assertNotEquals(null, createResult.user?.uid)
-        //assertEquals(null, createResult.user?.displayName)
-        //assertEquals(null, createResult.user?.phoneNumber)
+        // assertEquals(null, createResult.user?.displayName)
+        // assertEquals(null, createResult.user?.phoneNumber)
         assertEquals(false, createResult.user?.isAnonymous)
         assertEquals(email, createResult.user?.email)
 

From 4d2a74543685b2601a2e52b30a08dc2e028628fb Mon Sep 17 00:00:00 2001
From: nbransby <nbransby@gmail.com>
Date: Sat, 5 Oct 2024 13:47:29 +0800
Subject: [PATCH 14/23] remove * imports

---
 src/main/java/com/google/firebase/auth/FirebaseAuth.kt | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index 152963e..ee51e32 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -29,9 +29,15 @@ import kotlinx.serialization.json.intOrNull
 import kotlinx.serialization.json.jsonObject
 import kotlinx.serialization.json.jsonPrimitive
 import kotlinx.serialization.json.longOrNull
-import okhttp3.*
+import okhttp3.Call
+import okhttp3.Callback
+import okhttp3.MediaType
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.RequestBody
+import okhttp3.Response
 import java.io.IOException
-import java.util.*
+import java.util.Base64
 import java.util.concurrent.CopyOnWriteArrayList
 import java.util.concurrent.TimeUnit
 

From 76f990af00110d6070f6662c3240e4d68a90d686 Mon Sep 17 00:00:00 2001
From: Jakub Kostka <jacob.cube.mexx@gmail.com>
Date: Mon, 21 Oct 2024 15:53:14 +0200
Subject: [PATCH 15/23] updateEmail and verifyBeforeUpdateEmail implementation
 email is nullable again, check for empty string cancelation of token
 refresher after signout

---
 .../com/google/firebase/auth/FirebaseAuth.kt  | 332 +++++++++++-------
 .../com/google/firebase/auth/FirebaseUser.kt  |   6 +-
 .../google/firebase/auth/OobRequestType.kt    |   8 +
 src/test/kotlin/FirebaseAuthTest.kt           |  21 +-
 4 files changed, 234 insertions(+), 133 deletions(-)
 create mode 100644 src/main/java/com/google/firebase/auth/OobRequestType.kt

diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index ee51e32..0e94f5f 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -41,10 +41,10 @@ import java.util.Base64
 import java.util.concurrent.CopyOnWriteArrayList
 import java.util.concurrent.TimeUnit
 
-val jsonParser = Json { ignoreUnknownKeys = true }
+internal val jsonParser = Json { ignoreUnknownKeys = true }
 
 @Serializable
-class FirebaseUserImpl private constructor(
+class FirebaseUserImpl internal constructor(
     @Transient
     private val app: FirebaseApp = FirebaseApp.getInstance(),
     override val isAnonymous: Boolean,
@@ -53,14 +53,14 @@ class FirebaseUserImpl private constructor(
     val refreshToken: String,
     val expiresIn: Int,
     val createdAt: Long,
-    override val email: String
+    override val email: String?
 ) : FirebaseUser() {
 
     constructor(
         app: FirebaseApp,
         data: JsonObject,
         isAnonymous: Boolean = data["isAnonymous"]?.jsonPrimitive?.booleanOrNull ?: false,
-        email: String = data.getOrElse("email") { null }?.jsonPrimitive?.contentOrNull ?: ""
+        email: String? = data.getOrElse("email") { null }?.jsonPrimitive?.contentOrNull
     ) : this(
         app = app,
         isAnonymous = isAnonymous,
@@ -120,24 +120,109 @@ class FirebaseUserImpl private constructor(
         return source.task
     }
 
+    override fun updateEmail(email: String): Task<Unit> = FirebaseAuth.getInstance(app).updateEmail(email)
+
     override fun reload(): Task<Void> {
         val source = TaskCompletionSource<Void>()
         FirebaseAuth.getInstance(app).refreshToken(this, source) { null }
         return source.task
     }
 
+    override fun verifyBeforeUpdateEmail(
+        newEmail: String,
+        actionCodeSettings: ActionCodeSettings?
+    ): Task<Unit> {
+        val source = TaskCompletionSource<Unit>()
+        val body = RequestBody.create(
+            FirebaseAuth.getInstance(app).json,
+            JsonObject(
+                mapOf(
+                    "idToken" to JsonPrimitive(idToken),
+                    "email" to JsonPrimitive(email),
+                    "newEmail" to JsonPrimitive(newEmail),
+                    "requestType" to JsonPrimitive(OobRequestType.VERIFY_AND_CHANGE_EMAIL.name)
+                )
+            ).toString()
+        )
+        val request = Request.Builder()
+            .url("https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=" + app.options.apiKey)
+            .post(body)
+            .build()
+        FirebaseAuth.getInstance(app).client.newCall(request).enqueue(object : Callback {
+
+            override fun onFailure(call: Call, e: IOException) {
+                source.setException(FirebaseException(e.toString(), e))
+                e.printStackTrace()
+            }
+
+            @Throws(IOException::class)
+            override fun onResponse(call: Call, response: Response) {
+                if (!response.isSuccessful) {
+                    FirebaseAuth.getInstance(app).signOut()
+                    source.setException(
+                        FirebaseAuth.getInstance(app).createAuthInvalidUserException(
+                            "verifyEmail",
+                            request,
+                            response
+                        )
+                    )
+                } else {
+                    source.setResult(null)
+                }
+            }
+        })
+        return source.task
+    }
+
     override fun getIdToken(forceRefresh: Boolean) = FirebaseAuth.getInstance(app).getAccessToken(forceRefresh)
 }
 
 class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
 
-    val json = MediaType.parse("application/json; charset=utf-8")
-    val client: OkHttpClient = OkHttpClient.Builder()
+    internal val json = MediaType.parse("application/json; charset=utf-8")
+    internal val client: OkHttpClient = OkHttpClient.Builder()
         .connectTimeout(60, TimeUnit.SECONDS)
         .readTimeout(60, TimeUnit.SECONDS)
         .writeTimeout(60, TimeUnit.SECONDS)
         .build()
 
+    private fun enqueueAuthPost(
+        url: String,
+        body: RequestBody,
+        setResult: (responseBody: String) -> FirebaseUserImpl?
+    ): TaskCompletionSource<AuthResult> {
+        val source = TaskCompletionSource<AuthResult>()
+        val request = Request.Builder()
+            .url("$url?key=" + app.options.apiKey)
+            .post(body)
+            .build()
+
+        client.newCall(request).enqueue(object : Callback {
+            override fun onFailure(call: Call, e: IOException) {
+                source.setException(FirebaseException(e.toString(), e))
+            }
+
+            @Throws(IOException::class)
+            override fun onResponse(call: Call, response: Response) {
+                if (!response.isSuccessful) {
+                    source.setException(
+                        createAuthInvalidUserException("accounts", request, response)
+                    )
+                } else {
+                    if(response.body()?.use { it.string() }?.also { responseBody ->
+                        user = setResult(responseBody)
+                        source.setResult(AuthResult { user })
+                    } == null) {
+                        source.setException(
+                            createAuthInvalidUserException("accounts", request, response)
+                        )
+                    }
+                }
+            }
+        })
+        return source
+    }
+
     companion object {
 
         @JvmStatic
@@ -145,6 +230,8 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
 
         @JvmStatic
         fun getInstance(app: FirebaseApp): FirebaseAuth = app.get(FirebaseAuth::class.java)
+
+        private const val REFRESH_TOKEN_TAG = "refresh_token_tag"
     }
 
     private val internalIdTokenListeners = CopyOnWriteArrayList<com.google.firebase.auth.internal.IdTokenListener>()
@@ -192,127 +279,67 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
         }
 
     fun signInAnonymously(): Task<AuthResult> {
-        val source = TaskCompletionSource<AuthResult>()
-        val body = RequestBody.create(json, JsonObject(mapOf("returnSecureToken" to JsonPrimitive(true))).toString())
-        val request = Request.Builder()
-            .url("https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=" + app.options.apiKey)
-            .post(body)
-            .build()
-        client.newCall(request).enqueue(object : Callback {
-
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
+        val source = enqueueAuthPost(
+            url = "https://identitytoolkit.googleapis.com/v1/accounts:signUp",
+            body = RequestBody.create(json, JsonObject(mapOf("returnSecureToken" to JsonPrimitive(true))).toString()),
+            setResult = { responseBody ->
+                FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject, isAnonymous = true)
             }
-
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    source.setException(
-                        createAuthInvalidUserException("accounts:signUp", request, response)
-                    )
-                } else {
-                    val body = response.body()!!.use { it.string() }
-                    user = FirebaseUserImpl(app, jsonParser.parseToJsonElement(body).jsonObject, true)
-                    source.setResult(AuthResult { user })
-                }
-            }
-        })
+        )
         return source.task
     }
 
     fun signInWithCustomToken(customToken: String): Task<AuthResult> {
-        val source = TaskCompletionSource<AuthResult>()
-        val body = RequestBody.create(
-            json,
-            JsonObject(mapOf("token" to JsonPrimitive(customToken), "returnSecureToken" to JsonPrimitive(true))).toString()
-        )
-        val request = Request.Builder()
-            .url("https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken?key=" + app.options.apiKey)
-            .post(body)
-            .build()
-        client.newCall(request).enqueue(object : Callback {
-
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
+        val source = enqueueAuthPost(
+            url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken",
+            body = RequestBody.create(
+                json,
+                JsonObject(mapOf("token" to JsonPrimitive(customToken), "returnSecureToken" to JsonPrimitive(true))).toString()
+            ),
+            setResult = { responseBody ->
+                FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
             }
-
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    source.setException(
-                        createAuthInvalidUserException("verifyCustomToken", request, response)
-                    )
-                } else {
-                    val body = response.body()!!.use { it.string() }
-                    val user = FirebaseUserImpl(app, jsonParser.parseToJsonElement(body).jsonObject)
-                    refreshToken(user, source) { AuthResult { it } }
-                }
-            }
-        })
+        )
         return source.task
     }
 
     fun createUserWithEmailAndPassword(email: String, password: String): Task<AuthResult> {
-        val source = TaskCompletionSource<AuthResult>()
-        val body = RequestBody.create(
-            json,
-            JsonObject(mapOf("email" to JsonPrimitive(email), "password" to JsonPrimitive(password), "returnSecureToken" to JsonPrimitive(true))).toString()
-        )
-        val request = Request.Builder()
-            .url("https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=" + app.options.apiKey)
-            .post(body)
-            .build()
-        client.newCall(request).enqueue(object : Callback {
-
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
-            }
-
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    source.setException(
-                        createAuthInvalidUserException("signupNewUser", request, response)
+        val source = enqueueAuthPost(
+            url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser",
+            body = RequestBody.create(
+                json,
+                JsonObject(
+                    mapOf(
+                        "email" to JsonPrimitive(email),
+                        "password" to JsonPrimitive(password),
+                        "returnSecureToken" to JsonPrimitive(true)
                     )
-                } else {
-                    val responseBody = response.body()?.use { it.string() } ?: ""
-                    val user = FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
-                    refreshToken(user, source) { AuthResult { it } }
-                }
+                ).toString()
+            ),
+            setResult = { responseBody ->
+                FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
             }
-        })
+        )
         return source.task
     }
 
     fun signInWithEmailAndPassword(email: String, password: String): Task<AuthResult> {
-        val source = TaskCompletionSource<AuthResult>()
-        val body = RequestBody.create(
-            json,
-            JsonObject(mapOf("email" to JsonPrimitive(email), "password" to JsonPrimitive(password), "returnSecureToken" to JsonPrimitive(true))).toString()
-        )
-        val request = Request.Builder()
-            .url("https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=" + app.options.apiKey)
-            .post(body)
-            .build()
-        client.newCall(request).enqueue(object : Callback {
-
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
-            }
-
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    source.setException(
-                        createAuthInvalidUserException("verifyPassword", request, response)
+        val source = enqueueAuthPost(
+            url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword",
+            body = RequestBody.create(
+                json,
+                JsonObject(
+                    mapOf(
+                        "email" to JsonPrimitive(email),
+                        "password" to JsonPrimitive(password),
+                        "returnSecureToken" to JsonPrimitive(true)
                     )
-                } else {
-                    val body = response.body()!!.use { it.string() }
-                    val user = FirebaseUserImpl(app, jsonParser.parseToJsonElement(body).jsonObject)
-                    refreshToken(user, source) { AuthResult { it } }
-                }
+                ).toString()
+            ),
+            setResult = { responseBody ->
+                FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
             }
-        })
+        )
         return source.task
     }
 
@@ -336,7 +363,10 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
     }
 
     fun signOut() {
-        // todo cancel token refresher
+        // cancel token refresher
+        client.dispatcher().queuedCalls().find { it.request().tag() == REFRESH_TOKEN_TAG }?.cancel() ?: {
+            client.dispatcher().runningCalls().find { it.request().tag() == REFRESH_TOKEN_TAG }?.cancel()
+        }
         user = null
     }
 
@@ -377,6 +407,7 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
         val request = Request.Builder()
             .url("https://securetoken.googleapis.com/v1/token?key=" + app.options.apiKey)
             .post(body)
+            .tag(REFRESH_TOKEN_TAG)
             .build()
 
         client.newCall(request).enqueue(object : Callback {
@@ -387,20 +418,21 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
 
             @Throws(IOException::class)
             override fun onResponse(call: Call, response: Response) {
-                val body = response.body()?.use { it.string() }
-                if (!response.isSuccessful) {
-                    signOutAndThrowInvalidUserException(body.orEmpty(), "token API returned an error: $body")
+                val responseBody = response.body()?.use { it.string() }
+
+                if (!response.isSuccessful || responseBody == null) {
+                    signOutAndThrowInvalidUserException(responseBody.orEmpty(), "token API returned an error: $body")
                 } else {
-                    jsonParser.parseToJsonElement(body!!).jsonObject.apply {
-                        val user = FirebaseUserImpl(app, this, user.isAnonymous, user.email)
-                        if (user.claims["aud"] != app.options.projectId) {
+                    jsonParser.parseToJsonElement(responseBody).jsonObject.apply {
+                        val newUser = FirebaseUserImpl(app, this, user.isAnonymous, user.email)
+                        if (newUser.claims["aud"] != app.options.projectId) {
                             signOutAndThrowInvalidUserException(
-                                user.claims.toString(),
-                                "Project ID's do not match ${user.claims["aud"]} != ${app.options.projectId}"
+                                newUser.claims.toString(),
+                                "Project ID's do not match ${newUser.claims["aud"]} != ${app.options.projectId}"
                             )
                         } else {
-                            this@FirebaseAuth.user = user
-                            source.setResult(user)
+                            this@FirebaseAuth.user = newUser
+                            source.setResult(newUser)
                         }
                     }
                 }
@@ -414,6 +446,65 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
         return source
     }
 
+    internal fun updateEmail(email: String): Task<Unit> {
+        val source = TaskCompletionSource<Unit>()
+
+        val body = RequestBody.create(
+            json,
+            JsonObject(
+                mapOf(
+                    "idToken" to JsonPrimitive(user?.idToken),
+                    "email" to JsonPrimitive(email),
+                    "returnSecureToken" to JsonPrimitive(true)
+                )
+            ).toString()
+        )
+        val request = Request.Builder()
+            .url("https://identitytoolkit.googleapis.com/v1/accounts:update?key=" + app.options.apiKey)
+            .post(body)
+            .build()
+
+        client.newCall(request).enqueue(object : Callback {
+
+            override fun onFailure(call: Call, e: IOException) {
+                source.setException(FirebaseException(e.toString(), e))
+            }
+
+            @Throws(IOException::class)
+            override fun onResponse(call: Call, response: Response) {
+                if (!response.isSuccessful) {
+                    signOut()
+                    source.setException(
+                        createAuthInvalidUserException(
+                            "updateEmail",
+                            request,
+                            response
+                        )
+                    )
+                } else {
+                    val newBody = jsonParser.parseToJsonElement(
+                        response.body()?.use { it.string() } ?: ""
+                    ).jsonObject
+
+                    user?.let { prev ->
+                        user = FirebaseUserImpl(
+                            app = app,
+                            isAnonymous = prev.isAnonymous,
+                            uid = prev.uid,
+                            idToken = newBody["idToken"]?.jsonPrimitive?.contentOrNull ?: prev.idToken,
+                            refreshToken = newBody["refreshToken"]?.jsonPrimitive?.contentOrNull ?: prev.refreshToken,
+                            expiresIn = newBody["expiresIn"]?.jsonPrimitive?.intOrNull ?: prev.expiresIn,
+                            createdAt = prev.createdAt,
+                            email = newBody["newEmail"]?.jsonPrimitive?.contentOrNull ?: prev.email
+                        )
+                    }
+                    source.setResult(null)
+                }
+            }
+        })
+        return source.task
+    }
+
     override fun getUid(): String? {
         return user?.uid
     }
@@ -464,7 +555,6 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
     }
 
     fun sendPasswordResetEmail(email: String, settings: ActionCodeSettings?): Task<Unit> = TODO()
-    fun signInWithCredential(authCredential: AuthCredential): Task<AuthResult> = TODO()
     fun checkActionCode(code: String): Task<ActionCodeResult> = TODO()
     fun confirmPasswordReset(code: String, newPassword: String): Task<Unit> = TODO()
     fun fetchSignInMethodsForEmail(email: String): Task<SignInMethodQueryResult> = TODO()
diff --git a/src/main/java/com/google/firebase/auth/FirebaseUser.kt b/src/main/java/com/google/firebase/auth/FirebaseUser.kt
index c76c62b..50e4e52 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseUser.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseUser.kt
@@ -4,10 +4,12 @@ import com.google.android.gms.tasks.Task
 
 abstract class FirebaseUser {
     abstract val uid: String
+    abstract val email: String?
     abstract val isAnonymous: Boolean
     abstract fun delete(): Task<Void>
     abstract fun reload(): Task<Void>
-    abstract val email: String
+    abstract fun verifyBeforeUpdateEmail(newEmail: String, actionCodeSettings: ActionCodeSettings?): Task<Unit>
+    abstract fun updateEmail(email: String): Task<Unit>
 
     val displayName: String get() = TODO()
     val phoneNumber: String get() = TODO()
@@ -22,11 +24,9 @@ abstract class FirebaseUser {
     fun sendEmailVerification(): Task<Unit> = TODO()
     fun sendEmailVerification(actionCodeSettings: ActionCodeSettings): Task<Unit> = TODO()
     fun unlink(provider: String): Task<AuthResult> = TODO()
-    fun updateEmail(email: String): Task<Unit> = TODO()
     fun updatePassword(password: String): Task<Unit> = TODO()
     fun updatePhoneNumber(credential: AuthCredential): Task<Unit> = TODO()
     fun updateProfile(request: UserProfileChangeRequest): Task<Unit> = TODO()
-    fun verifyBeforeUpdateEmail(newEmail: String, actionCodeSettings: ActionCodeSettings?): Task<Unit> = TODO()
     fun reauthenticate(credential: AuthCredential): Task<Unit> = TODO()
     fun reauthenticateAndRetrieveData(credential: AuthCredential): Task<AuthResult> = TODO()
 }
diff --git a/src/main/java/com/google/firebase/auth/OobRequestType.kt b/src/main/java/com/google/firebase/auth/OobRequestType.kt
new file mode 100644
index 0000000..f0c31c3
--- /dev/null
+++ b/src/main/java/com/google/firebase/auth/OobRequestType.kt
@@ -0,0 +1,8 @@
+package com.google.firebase.auth
+
+internal enum class OobRequestType {
+    PASSWORD_RESET,
+    EMAIL_SIGNIN,
+    VERIFY_EMAIL,
+    VERIFY_AND_CHANGE_EMAIL
+}
\ No newline at end of file
diff --git a/src/test/kotlin/FirebaseAuthTest.kt b/src/test/kotlin/FirebaseAuthTest.kt
index 2925db4..029cd11 100644
--- a/src/test/kotlin/FirebaseAuthTest.kt
+++ b/src/test/kotlin/FirebaseAuthTest.kt
@@ -1,3 +1,4 @@
+
 import android.app.Application
 import com.google.firebase.Firebase
 import com.google.firebase.FirebaseOptions
@@ -6,12 +7,13 @@ import com.google.firebase.auth.FirebaseAuth
 import com.google.firebase.initialize
 import kotlinx.coroutines.tasks.await
 import kotlinx.coroutines.test.runTest
+import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
 import org.junit.Before
 import org.junit.Test
 import java.io.File
-import kotlin.random.Random
+import java.util.UUID
 
 internal class FirebaseAuthTest : FirebaseTest() {
 
@@ -40,29 +42,30 @@ internal class FirebaseAuthTest : FirebaseTest() {
         auth = FirebaseAuth.getInstance(app = firebaseApp)
     }
 
+    @After
+    fun clear() {
+        auth.currentUser?.delete()
+    }
+
     @Test
     fun testCreateUserWithEmailAndPassword() = runTest {
-        val email = "test+${Random.nextInt(100000)}@test.com"
-        val createResult = auth.createUserWithEmailAndPassword(
-            email,
-            "test123"
-        ).await()
+        val email = "test+${UUID.randomUUID()}@test.com"
+        val createResult = auth.createUserWithEmailAndPassword(email, "test123").await()
         assertNotEquals(null, createResult.user?.uid)
         // assertEquals(null, createResult.user?.displayName)
         // assertEquals(null, createResult.user?.phoneNumber)
         assertEquals(false, createResult.user?.isAnonymous)
         assertEquals(email, createResult.user?.email)
+        assertNotEquals("", createResult.user!!.email)
 
         val signInResult = auth.signInWithEmailAndPassword(email, "test123").await()
         assertEquals(createResult.user?.uid, signInResult.user?.uid)
-
-        signInResult.user!!.delete()
     }
 
     @Test
     fun testSignInAnonymously() = runTest {
         val signInResult = auth.signInAnonymously().await()
+        assertNotEquals("", signInResult.user!!.email)
         assertEquals(true, signInResult.user?.isAnonymous)
-        signInResult.user!!.delete()
     }
 }

From 694da886f10c8761b94576395a5bb0fd899fb99f Mon Sep 17 00:00:00 2001
From: Jakub Kostka <jacob.cube.mexx@gmail.com>
Date: Mon, 21 Oct 2024 16:02:26 +0200
Subject: [PATCH 16/23] added todo

---
 src/main/java/com/google/firebase/auth/FirebaseAuth.kt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index 0e94f5f..ba7543b 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -128,6 +128,7 @@ class FirebaseUserImpl internal constructor(
         return source.task
     }
 
+    //TODO implement ActionCodeSettings and pass it to the url
     override fun verifyBeforeUpdateEmail(
         newEmail: String,
         actionCodeSettings: ActionCodeSettings?

From 3dea253404b5799f6d7926027e1294e6e5aec3d7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Franti=C5=A1ek=20Ma=C5=A1a?= <frantisekmasa1@gmail.com>
Date: Mon, 7 Oct 2024 23:26:22 +0200
Subject: [PATCH 17/23] Test Auth against emulator

---
 .firebaserc                                   |  1 +
 .github/workflows/build-pr.yml                | 10 ++--
 firebase.json                                 | 11 +++++
 .../com/google/firebase/auth/FirebaseAuth.kt  | 42 ++++++++++++-----
 src/test/kotlin/AuthTest.kt                   | 47 +++++++++++++++++++
 src/test/kotlin/FirebaseTest.kt               | 24 ++++++++++
 src/test/kotlin/FirestoreTest.kt              | 31 ++----------
 .../firebase_data/auth_export/accounts.json   | 29 ++++++++++++
 .../firebase_data/auth_export/config.json     |  8 ++++
 .../firebase-export-metadata.json             |  7 +++
 10 files changed, 169 insertions(+), 41 deletions(-)
 create mode 100644 .firebaserc
 create mode 100644 firebase.json
 create mode 100644 src/test/kotlin/AuthTest.kt
 create mode 100644 src/test/resources/firebase_data/auth_export/accounts.json
 create mode 100644 src/test/resources/firebase_data/auth_export/config.json
 create mode 100644 src/test/resources/firebase_data/firebase-export-metadata.json

diff --git a/.firebaserc b/.firebaserc
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/.firebaserc
@@ -0,0 +1 @@
+{}
diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml
index 15b5ace..028ce50 100644
--- a/.github/workflows/build-pr.yml
+++ b/.github/workflows/build-pr.yml
@@ -11,7 +11,11 @@ jobs:
         with:
           distribution: 'zulu'
           java-version: 17
-      - name: Build
-        uses: eskatos/gradle-command-action@v3
+      - name: Set up Node.js 20
+        uses: actions/setup-node@v4
         with:
-          arguments: build
+          node-version: 20
+      - name: Install Firebase CLI
+        run: npm install -g firebase-tools
+      - name: Build
+        run: firebase emulators:exec --project my-firebase-project --import=src/test/resources/firebase_data './gradlew build'
diff --git a/firebase.json b/firebase.json
new file mode 100644
index 0000000..2fb2a16
--- /dev/null
+++ b/firebase.json
@@ -0,0 +1,11 @@
+{
+  "emulators": {
+    "auth": {
+      "port": 9099
+    },
+    "ui": {
+      "enabled": true
+    },
+    "singleProjectMode": true
+  }
+}
diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index ba7543b..c80e2a1 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -43,6 +43,15 @@ import java.util.concurrent.TimeUnit
 
 internal val jsonParser = Json { ignoreUnknownKeys = true }
 
+class UrlFactory(
+    private val app: FirebaseApp,
+    private val emulatorUrl: String? = null
+) {
+    fun buildUrl(uri: String): String {
+        return "${emulatorUrl ?: "https://"}$uri?key=${app.options.apiKey}"
+    }
+}
+
 @Serializable
 class FirebaseUserImpl internal constructor(
     @Transient
@@ -53,14 +62,17 @@ class FirebaseUserImpl internal constructor(
     val refreshToken: String,
     val expiresIn: Int,
     val createdAt: Long,
-    override val email: String?
+    override val email: String?,
+    @Transient
+    private val urlFactory: UrlFactory = UrlFactory(app)
 ) : FirebaseUser() {
 
     constructor(
         app: FirebaseApp,
         data: JsonObject,
         isAnonymous: Boolean = data["isAnonymous"]?.jsonPrimitive?.booleanOrNull ?: false,
-        email: String? = data.getOrElse("email") { null }?.jsonPrimitive?.contentOrNull
+        email: String? = data.getOrElse("email") { null }?.jsonPrimitive?.contentOrNull,
+        urlFactory: UrlFactory = UrlFactory(app)
     ) : this(
         app = app,
         isAnonymous = isAnonymous,
@@ -69,7 +81,8 @@ class FirebaseUserImpl internal constructor(
         refreshToken = data["refreshToken"]?.jsonPrimitive?.contentOrNull ?: data.getValue("refresh_token").jsonPrimitive.content,
         expiresIn = data["expiresIn"]?.jsonPrimitive?.intOrNull ?: data.getValue("expires_in").jsonPrimitive.int,
         createdAt = data["createdAt"]?.jsonPrimitive?.longOrNull ?: System.currentTimeMillis(),
-        email = email
+        email = email,
+        urlFactory = urlFactory
     )
 
     val claims: Map<String, Any?> by lazy {
@@ -92,7 +105,7 @@ class FirebaseUserImpl internal constructor(
         val source = TaskCompletionSource<Void>()
         val body = RequestBody.create(FirebaseAuth.getInstance(app).json, JsonObject(mapOf("idToken" to JsonPrimitive(idToken))).toString())
         val request = Request.Builder()
-            .url("https://www.googleapis.com/identitytoolkit/v3/relyingparty/deleteAccount?key=" + app.options.apiKey)
+            .url(urlFactory.buildUrl("www.googleapis.com/identitytoolkit/v3/relyingparty/deleteAccount"))
             .post(body)
             .build()
         FirebaseAuth.getInstance(app).client.newCall(request).enqueue(object : Callback {
@@ -194,7 +207,7 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
     ): TaskCompletionSource<AuthResult> {
         val source = TaskCompletionSource<AuthResult>()
         val request = Request.Builder()
-            .url("$url?key=" + app.options.apiKey)
+            .url(urlFactory.buildUrl(url))
             .post(body)
             .build()
 
@@ -279,9 +292,11 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
             }
         }
 
+    private var urlFactory = UrlFactory(app)
+
     fun signInAnonymously(): Task<AuthResult> {
         val source = enqueueAuthPost(
-            url = "https://identitytoolkit.googleapis.com/v1/accounts:signUp",
+            url = "identitytoolkit.googleapis.com/v1/accounts:signUp",
             body = RequestBody.create(json, JsonObject(mapOf("returnSecureToken" to JsonPrimitive(true))).toString()),
             setResult = { responseBody ->
                 FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject, isAnonymous = true)
@@ -292,7 +307,7 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
 
     fun signInWithCustomToken(customToken: String): Task<AuthResult> {
         val source = enqueueAuthPost(
-            url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken",
+            url = "www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken",
             body = RequestBody.create(
                 json,
                 JsonObject(mapOf("token" to JsonPrimitive(customToken), "returnSecureToken" to JsonPrimitive(true))).toString()
@@ -306,7 +321,7 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
 
     fun createUserWithEmailAndPassword(email: String, password: String): Task<AuthResult> {
         val source = enqueueAuthPost(
-            url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser",
+            url = "www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser",
             body = RequestBody.create(
                 json,
                 JsonObject(
@@ -324,9 +339,10 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
         return source.task
     }
 
+
     fun signInWithEmailAndPassword(email: String, password: String): Task<AuthResult> {
         val source = enqueueAuthPost(
-            url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword",
+            url = "www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword",
             body = RequestBody.create(
                 json,
                 JsonObject(
@@ -406,7 +422,7 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
             ).toString()
         )
         val request = Request.Builder()
-            .url("https://securetoken.googleapis.com/v1/token?key=" + app.options.apiKey)
+            .url(urlFactory.buildUrl("securetoken.googleapis.com/v1/token"))
             .post(body)
             .tag(REFRESH_TOKEN_TAG)
             .build()
@@ -555,7 +571,12 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
         idTokenListeners.remove(listener)
     }
 
+    fun useEmulator(host: String, port: Int) {
+        urlFactory = UrlFactory(app, "http://$host:$port/")
+    }
+
     fun sendPasswordResetEmail(email: String, settings: ActionCodeSettings?): Task<Unit> = TODO()
+    fun signInWithCredential(authCredential: AuthCredential): Task<AuthResult> = TODO()
     fun checkActionCode(code: String): Task<ActionCodeResult> = TODO()
     fun confirmPasswordReset(code: String, newPassword: String): Task<Unit> = TODO()
     fun fetchSignInMethodsForEmail(email: String): Task<SignInMethodQueryResult> = TODO()
@@ -568,5 +589,4 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
     fun signInWithEmailLink(email: String, link: String): Task<AuthResult> = TODO()
 
     fun setLanguageCode(value: String): Nothing = TODO()
-    fun useEmulator(host: String, port: Int): Unit = TODO()
 }
diff --git a/src/test/kotlin/AuthTest.kt b/src/test/kotlin/AuthTest.kt
new file mode 100644
index 0000000..7bdb90c
--- /dev/null
+++ b/src/test/kotlin/AuthTest.kt
@@ -0,0 +1,47 @@
+import com.google.firebase.auth.FirebaseAuth
+import com.google.firebase.auth.FirebaseAuthInvalidUserException
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.tasks.await
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThrows
+import org.junit.Test
+
+class AuthTest : FirebaseTest() {
+    private fun createAuth(): FirebaseAuth {
+        return FirebaseAuth(app).apply {
+            useEmulator("localhost", 9099)
+        }
+    }
+
+    @Test
+    fun `should authenticate via anonymous auth`() = runTest {
+        val auth = createAuth()
+
+        auth.signInAnonymously().await()
+
+        assertEquals(true, auth.currentUser?.isAnonymous)
+    }
+
+    @Test
+    fun `should authenticate via email and password`() = runTest {
+        val auth = createAuth()
+
+        auth.signInWithEmailAndPassword("email@example.com", "securepassword").await()
+
+        assertEquals(false, auth.currentUser?.isAnonymous)
+    }
+
+    @Test
+    fun `should throw exception on invalid password`() {
+        val auth = createAuth()
+
+        val exception = assertThrows(FirebaseAuthInvalidUserException::class.java) {
+            runBlocking {
+                auth.signInWithEmailAndPassword("email@example.com", "wrongpassword").await()
+            }
+        }
+
+        assertEquals("INVALID_PASSWORD", exception.errorCode)
+    }
+}
diff --git a/src/test/kotlin/FirebaseTest.kt b/src/test/kotlin/FirebaseTest.kt
index 224b473..77aa858 100644
--- a/src/test/kotlin/FirebaseTest.kt
+++ b/src/test/kotlin/FirebaseTest.kt
@@ -1,9 +1,33 @@
+import android.app.Application
+import com.google.firebase.Firebase
 import com.google.firebase.FirebaseApp
+import com.google.firebase.FirebaseOptions
+import com.google.firebase.FirebasePlatform
+import com.google.firebase.initialize
 import org.junit.Before
+import java.io.File
 
 abstract class FirebaseTest {
+    protected val app: FirebaseApp get() {
+        val options = FirebaseOptions.Builder()
+            .setProjectId("my-firebase-project")
+            .setApplicationId("1:27992087142:android:ce3b6448250083d1")
+            .setApiKey("AIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
+            .build()
+
+        return Firebase.initialize(Application(), options)
+    }
+
     @Before
     fun beforeEach() {
+        FirebasePlatform.initializeFirebasePlatform(object : FirebasePlatform() {
+            val storage = mutableMapOf<String, String>()
+            override fun store(key: String, value: String) = storage.set(key, value)
+            override fun retrieve(key: String) = storage[key]
+            override fun clear(key: String) { storage.remove(key) }
+            override fun log(msg: String) = println(msg)
+            override fun getDatabasePath(name: String) = File("./build/$name")
+        })
         FirebaseApp.clearInstancesForTest()
     }
 }
diff --git a/src/test/kotlin/FirestoreTest.kt b/src/test/kotlin/FirestoreTest.kt
index 95afc48..f3a8113 100644
--- a/src/test/kotlin/FirestoreTest.kt
+++ b/src/test/kotlin/FirestoreTest.kt
@@ -1,42 +1,19 @@
-import android.app.Application
 import com.google.firebase.Firebase
-import com.google.firebase.FirebaseOptions
-import com.google.firebase.FirebasePlatform
 import com.google.firebase.firestore.firestore
-import com.google.firebase.initialize
 import kotlinx.coroutines.tasks.await
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
-import org.junit.Before
 import org.junit.Test
-import java.io.File
 
 class FirestoreTest : FirebaseTest() {
-    @Before
-    fun initialize() {
-        FirebasePlatform.initializeFirebasePlatform(object : FirebasePlatform() {
-            val storage = mutableMapOf<String, String>()
-            override fun store(key: String, value: String) = storage.set(key, value)
-            override fun retrieve(key: String) = storage[key]
-            override fun clear(key: String) { storage.remove(key) }
-            override fun log(msg: String) = println(msg)
-            override fun getDatabasePath(name: String) = File("./build/$name")
-        })
-        val options = FirebaseOptions.Builder()
-            .setProjectId("my-firebase-project")
-            .setApplicationId("1:27992087142:android:ce3b6448250083d1")
-            .setApiKey("AIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
-            // setDatabaseURL(...)
-            // setStorageBucket(...)
-            .build()
-        Firebase.initialize(Application(), options)
-        Firebase.firestore.disableNetwork()
-    }
 
     @Test
     fun testFirestore(): Unit = runTest {
+        val firestore = Firebase.firestore(app)
+        firestore.disableNetwork().await()
+
         val data = Data("jim")
-        val doc = Firebase.firestore.document("sally/jim")
+        val doc = firestore.document("sally/jim")
         doc.set(data)
         assertEquals(data, doc.get().await().toObject(Data::class.java))
     }
diff --git a/src/test/resources/firebase_data/auth_export/accounts.json b/src/test/resources/firebase_data/auth_export/accounts.json
new file mode 100644
index 0000000..3b70f06
--- /dev/null
+++ b/src/test/resources/firebase_data/auth_export/accounts.json
@@ -0,0 +1,29 @@
+{
+  "kind": "identitytoolkit#DownloadAccountResponse",
+  "users": [
+    {
+      "localId": "Ijat10t0F1gvH1VrClkkSqEcId1p",
+      "lastLoginAt": "1728509249920",
+      "displayName": "",
+      "photoUrl": "",
+      "emailVerified": true,
+      "email": "email@example.com",
+      "salt": "fakeSaltHsRxYqy9iKVQRLwz8975",
+      "passwordHash": "fakeHash:salt=fakeSaltHsRxYqy9iKVQRLwz8975:password=securepassword",
+      "passwordUpdatedAt": 1728509249921,
+      "validSince": "1728509249",
+      "mfaInfo": [],
+      "createdAt": "1728509249920",
+      "providerUserInfo": [
+        {
+          "providerId": "password",
+          "email": "email@example.com",
+          "federatedId": "email@example.com",
+          "rawId": "email@example.com",
+          "displayName": "",
+          "photoUrl": ""
+        }
+      ]
+    }
+  ]
+}
diff --git a/src/test/resources/firebase_data/auth_export/config.json b/src/test/resources/firebase_data/auth_export/config.json
new file mode 100644
index 0000000..9e07e93
--- /dev/null
+++ b/src/test/resources/firebase_data/auth_export/config.json
@@ -0,0 +1,8 @@
+{
+  "signIn": {
+    "allowDuplicateEmails": false
+  },
+  "emailPrivacyConfig": {
+    "enableImprovedEmailPrivacy": false
+  }
+}
diff --git a/src/test/resources/firebase_data/firebase-export-metadata.json b/src/test/resources/firebase_data/firebase-export-metadata.json
new file mode 100644
index 0000000..13a607f
--- /dev/null
+++ b/src/test/resources/firebase_data/firebase-export-metadata.json
@@ -0,0 +1,7 @@
+{
+  "version": "13.3.1",
+  "auth": {
+    "version": "13.3.1",
+    "path": "auth_export"
+  }
+}
\ No newline at end of file

From c811e394066cad9ab1d503237112bd7dacff6c8c Mon Sep 17 00:00:00 2001
From: Jakub Kostka <jacob.cube.mexx@gmail.com>
Date: Thu, 7 Nov 2024 16:11:35 +0100
Subject: [PATCH 18/23] ktlint formatting + update of user data after
 signInWithCustomToken + photoUrl and displayName implementation

---
 src/main/java/android/net/Uri.kt              |   7 +-
 .../com/google/firebase/auth/FirebaseAuth.kt  | 772 +++++++++++++-----
 .../com/google/firebase/auth/FirebaseUser.kt  |  27 +-
 .../google/firebase/auth/OobRequestType.kt    |   2 +-
 .../auth/UserProfileChangeRequest.java        |  18 -
 .../firebase/auth/UserProfileChangeRequest.kt |  47 ++
 src/test/kotlin/AppTest.kt                    |  42 +-
 src/test/kotlin/AuthTest.kt                   |  92 ++-
 src/test/kotlin/FirebaseAuthTest.kt           | 100 +--
 src/test/kotlin/FirebaseTest.kt               |  67 +-
 10 files changed, 846 insertions(+), 328 deletions(-)
 delete mode 100644 src/main/java/com/google/firebase/auth/UserProfileChangeRequest.java
 create mode 100644 src/main/java/com/google/firebase/auth/UserProfileChangeRequest.kt

diff --git a/src/main/java/android/net/Uri.kt b/src/main/java/android/net/Uri.kt
index 851faa2..c14bc76 100644
--- a/src/main/java/android/net/Uri.kt
+++ b/src/main/java/android/net/Uri.kt
@@ -3,13 +3,16 @@ package android.net
 import java.net.URI
 import java.util.Collections
 
-class Uri(private val uri: URI) {
-
+class Uri(
+    private val uri: URI,
+) {
     companion object {
         @JvmStatic
         fun parse(uriString: String) = Uri(URI.create(uriString))
     }
 
+    override fun toString(): String = uri.toString()
+
     val scheme get() = uri.scheme
     val port get() = uri.port
     val host get() = uri.host
diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index c80e2a1..11e2db4 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -1,5 +1,6 @@
 package com.google.firebase.auth
 
+import android.net.Uri
 import android.util.Log
 import com.google.android.gms.tasks.Task
 import com.google.android.gms.tasks.TaskCompletionSource
@@ -45,11 +46,9 @@ internal val jsonParser = Json { ignoreUnknownKeys = true }
 
 class UrlFactory(
     private val app: FirebaseApp,
-    private val emulatorUrl: String? = null
+    private val emulatorUrl: String? = null,
 ) {
-    fun buildUrl(uri: String): String {
-        return "${emulatorUrl ?: "https://"}$uri?key=${app.options.apiKey}"
-    }
+    fun buildUrl(uri: String): String = "${emulatorUrl ?: "https://"}$uri?key=${app.options.apiKey}"
 }
 
 @Serializable
@@ -63,26 +62,34 @@ class FirebaseUserImpl internal constructor(
     val expiresIn: Int,
     val createdAt: Long,
     override val email: String?,
+    override val photoUrl: String?,
+    override val displayName: String?,
     @Transient
-    private val urlFactory: UrlFactory = UrlFactory(app)
+    private val urlFactory: UrlFactory = UrlFactory(app),
 ) : FirebaseUser() {
-
     constructor(
         app: FirebaseApp,
         data: JsonObject,
         isAnonymous: Boolean = data["isAnonymous"]?.jsonPrimitive?.booleanOrNull ?: false,
         email: String? = data.getOrElse("email") { null }?.jsonPrimitive?.contentOrNull,
-        urlFactory: UrlFactory = UrlFactory(app)
+        photoUrl: String? = data.getOrElse("photoUrl") { null }?.jsonPrimitive?.contentOrNull,
+        displayName: String? = data.getOrElse("displayName") { null }?.jsonPrimitive?.contentOrNull,
+        urlFactory: UrlFactory = UrlFactory(app),
     ) : this(
         app = app,
         isAnonymous = isAnonymous,
-        uid = data["uid"]?.jsonPrimitive?.contentOrNull ?: data["user_id"]?.jsonPrimitive?.contentOrNull ?: data["localId"]?.jsonPrimitive?.contentOrNull ?: "",
+        uid =
+            data["uid"]?.jsonPrimitive?.contentOrNull ?: data["user_id"]?.jsonPrimitive?.contentOrNull
+                ?: data["localId"]?.jsonPrimitive?.contentOrNull
+                ?: "",
         idToken = data["idToken"]?.jsonPrimitive?.contentOrNull ?: data.getValue("id_token").jsonPrimitive.content,
         refreshToken = data["refreshToken"]?.jsonPrimitive?.contentOrNull ?: data.getValue("refresh_token").jsonPrimitive.content,
         expiresIn = data["expiresIn"]?.jsonPrimitive?.intOrNull ?: data.getValue("expires_in").jsonPrimitive.int,
         createdAt = data["createdAt"]?.jsonPrimitive?.longOrNull ?: System.currentTimeMillis(),
         email = email,
-        urlFactory = urlFactory
+        photoUrl = photoUrl ?: data["photo_url"]?.jsonPrimitive?.contentOrNull,
+        displayName = displayName ?: data["display_name"]?.jsonPrimitive?.contentOrNull,
+        urlFactory = urlFactory,
     )
 
     val claims: Map<String, Any?> by lazy {
@@ -93,6 +100,7 @@ class FirebaseUserImpl internal constructor(
             .orEmpty()
     }
 
+<<<<<<< HEAD
     val JsonElement.value get(): Any? = when (this) {
         is JsonNull -> null
         is JsonArray -> map { it.value }
@@ -100,36 +108,55 @@ class FirebaseUserImpl internal constructor(
         is JsonPrimitive -> booleanOrNull ?: doubleOrNull ?: content
         else -> TODO()
     }
+=======
+    internal val JsonElement.value get(): Any? =
+        when (this) {
+            is JsonNull -> null
+            is JsonArray -> map { it.value }
+            is JsonObject -> jsonObject.mapValues { (_, it) -> it.value }
+            is JsonPrimitive -> booleanOrNull ?: doubleOrNull ?: content
+            else -> TODO()
+        }
+>>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
 
     override fun delete(): Task<Void> {
         val source = TaskCompletionSource<Void>()
         val body = RequestBody.create(FirebaseAuth.getInstance(app).json, JsonObject(mapOf("idToken" to JsonPrimitive(idToken))).toString())
-        val request = Request.Builder()
-            .url(urlFactory.buildUrl("www.googleapis.com/identitytoolkit/v3/relyingparty/deleteAccount"))
-            .post(body)
-            .build()
-        FirebaseAuth.getInstance(app).client.newCall(request).enqueue(object : Callback {
-
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
-            }
+        val request =
+            Request
+                .Builder()
+                .url(urlFactory.buildUrl("www.googleapis.com/identitytoolkit/v3/relyingparty/deleteAccount"))
+                .post(body)
+                .build()
+        FirebaseAuth.getInstance(app).client.newCall(request).enqueue(
+            object : Callback {
+                override fun onFailure(
+                    call: Call,
+                    e: IOException,
+                ) {
+                    source.setException(FirebaseException(e.toString(), e))
+                }
 
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    FirebaseAuth.getInstance(app).signOut()
-                    source.setException(
-                        FirebaseAuth.getInstance(app).createAuthInvalidUserException(
-                            "deleteAccount",
-                            request,
-                            response
+                @Throws(IOException::class)
+                override fun onResponse(
+                    call: Call,
+                    response: Response,
+                ) {
+                    if (!response.isSuccessful) {
+                        FirebaseAuth.getInstance(app).signOut()
+                        source.setException(
+                            FirebaseAuth.getInstance(app).createAuthInvalidUserException(
+                                "deleteAccount",
+                                request,
+                                response,
+                            ),
                         )
-                    )
-                } else {
-                    source.setResult(null)
+                    } else {
+                        source.setResult(null)
+                    }
                 }
-            }
-        })
+            },
+        )
         return source.task
     }
 
@@ -141,81 +168,123 @@ class FirebaseUserImpl internal constructor(
         return source.task
     }
 
-    //TODO implement ActionCodeSettings and pass it to the url
+    // TODO implement ActionCodeSettings and pass it to the url
     override fun verifyBeforeUpdateEmail(
         newEmail: String,
-        actionCodeSettings: ActionCodeSettings?
+        actionCodeSettings: ActionCodeSettings?,
     ): Task<Unit> {
         val source = TaskCompletionSource<Unit>()
-        val body = RequestBody.create(
-            FirebaseAuth.getInstance(app).json,
-            JsonObject(
-                mapOf(
-                    "idToken" to JsonPrimitive(idToken),
-                    "email" to JsonPrimitive(email),
-                    "newEmail" to JsonPrimitive(newEmail),
-                    "requestType" to JsonPrimitive(OobRequestType.VERIFY_AND_CHANGE_EMAIL.name)
-                )
-            ).toString()
-        )
-        val request = Request.Builder()
-            .url("https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=" + app.options.apiKey)
-            .post(body)
-            .build()
-        FirebaseAuth.getInstance(app).client.newCall(request).enqueue(object : Callback {
-
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
-                e.printStackTrace()
-            }
+        val body =
+            RequestBody.create(
+                FirebaseAuth.getInstance(app).json,
+                JsonObject(
+                    mapOf(
+                        "idToken" to JsonPrimitive(idToken),
+                        "email" to JsonPrimitive(email),
+                        "newEmail" to JsonPrimitive(newEmail),
+                        "requestType" to JsonPrimitive(OobRequestType.VERIFY_AND_CHANGE_EMAIL.name),
+                    ),
+                ).toString(),
+            )
+        val request =
+            Request
+                .Builder()
+                .url(urlFactory.buildUrl("identitytoolkit.googleapis.com/v1/accounts:sendOobCode"))
+                .post(body)
+                .build()
+        FirebaseAuth.getInstance(app).client.newCall(request).enqueue(
+            object : Callback {
+                override fun onFailure(
+                    call: Call,
+                    e: IOException,
+                ) {
+                    source.setException(FirebaseException(e.toString(), e))
+                    e.printStackTrace()
+                }
 
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    FirebaseAuth.getInstance(app).signOut()
-                    source.setException(
-                        FirebaseAuth.getInstance(app).createAuthInvalidUserException(
-                            "verifyEmail",
-                            request,
-                            response
+                @Throws(IOException::class)
+                override fun onResponse(
+                    call: Call,
+                    response: Response,
+                ) {
+                    if (!response.isSuccessful) {
+                        FirebaseAuth.getInstance(app).signOut()
+                        source.setException(
+                            FirebaseAuth.getInstance(app).createAuthInvalidUserException(
+                                "verifyEmail",
+                                request,
+                                response,
+                            ),
                         )
-                    )
-                } else {
-                    source.setResult(null)
+                    } else {
+                        source.setResult(null)
+                    }
                 }
-            }
-        })
+            },
+        )
         return source.task
     }
 
     override fun getIdToken(forceRefresh: Boolean) = FirebaseAuth.getInstance(app).getAccessToken(forceRefresh)
-}
 
-class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
+    override fun updateProfile(request: UserProfileChangeRequest): Task<Unit> = FirebaseAuth.getInstance(app).updateProfile(request)
+
+    fun updateProfile(
+        displayName: String?,
+        photoUrl: String?,
+    ): Task<Unit> {
+        val request =
+            UserProfileChangeRequest
+                .Builder()
+                .apply { setDisplayName(displayName) }
+                .apply { setPhotoUri(photoUrl?.let { Uri.parse(it) }) }
+                .build()
+        return FirebaseAuth.getInstance(app).updateProfile(request)
+    }
+}
 
+class FirebaseAuth constructor(
+    val app: FirebaseApp,
+) : InternalAuthProvider {
     internal val json = MediaType.parse("application/json; charset=utf-8")
-    internal val client: OkHttpClient = OkHttpClient.Builder()
-        .connectTimeout(60, TimeUnit.SECONDS)
-        .readTimeout(60, TimeUnit.SECONDS)
-        .writeTimeout(60, TimeUnit.SECONDS)
-        .build()
+    internal val client: OkHttpClient =
+        OkHttpClient
+            .Builder()
+            .connectTimeout(60, TimeUnit.SECONDS)
+            .readTimeout(60, TimeUnit.SECONDS)
+            .writeTimeout(60, TimeUnit.SECONDS)
+            .build()
 
     private fun enqueueAuthPost(
         url: String,
         body: RequestBody,
-        setResult: (responseBody: String) -> FirebaseUserImpl?
+        setResult: (responseBody: String) -> FirebaseUserImpl?,
     ): TaskCompletionSource<AuthResult> {
         val source = TaskCompletionSource<AuthResult>()
+<<<<<<< HEAD
         val request = Request.Builder()
             .url(urlFactory.buildUrl(url))
             .post(body)
             .build()
+=======
+        val request =
+            Request
+                .Builder()
+                .url(urlFactory.buildUrl(url))
+                .post(body)
+                .build()
+>>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
+
+        client.newCall(request).enqueue(
+            object : Callback {
+                override fun onFailure(
+                    call: Call,
+                    e: IOException,
+                ) {
+                    source.setException(FirebaseException(e.toString(), e))
+                }
 
-        client.newCall(request).enqueue(object : Callback {
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
-            }
-
+<<<<<<< HEAD
             @Throws(IOException::class)
             override fun onResponse(call: Call, response: Response) {
                 if (!response.isSuccessful) {
@@ -227,18 +296,35 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
                         user = setResult(responseBody)
                         source.setResult(AuthResult { user })
                     } == null) {
+=======
+                @Throws(IOException::class)
+                override fun onResponse(
+                    call: Call,
+                    response: Response,
+                ) {
+                    if (!response.isSuccessful) {
+>>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
                         source.setException(
-                            createAuthInvalidUserException("accounts", request, response)
+                            createAuthInvalidUserException("accounts", request, response),
                         )
+                    } else {
+                        if (response.body()?.use { it.string() }?.also { responseBody ->
+                                user = setResult(responseBody)
+                                source.setResult(AuthResult { user })
+                            } == null
+                        ) {
+                            source.setException(
+                                createAuthInvalidUserException("accounts", request, response),
+                            )
+                        }
                     }
                 }
-            }
-        })
+            },
+        )
         return source
     }
 
     companion object {
-
         @JvmStatic
         fun getInstance(): FirebaseAuth = getInstance(FirebaseApp.getInstance())
 
@@ -257,10 +343,11 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
 
     val FirebaseApp.key get() = "com.google.firebase.auth.FIREBASE_USER${"[$name]".takeUnless { isDefaultApp }.orEmpty()}"
 
-    private var user: FirebaseUserImpl? = FirebasePlatform.firebasePlatform
-        .runCatching { retrieve(app.key)?.let { FirebaseUserImpl(app, jsonParser.parseToJsonElement(it).jsonObject) } }
-        .onFailure { it.printStackTrace() }
-        .getOrNull()
+    private var user: FirebaseUserImpl? =
+        FirebasePlatform.firebasePlatform
+            .runCatching { retrieve(app.key)?.let { FirebaseUserImpl(app, data = jsonParser.parseToJsonElement(it).jsonObject) } }
+            .onFailure { it.printStackTrace() }
+            .getOrNull()
 
         private set(value) {
             if (field != value) {
@@ -295,6 +382,7 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
     private var urlFactory = UrlFactory(app)
 
     fun signInAnonymously(): Task<AuthResult> {
+<<<<<<< HEAD
         val source = enqueueAuthPost(
             url = "identitytoolkit.googleapis.com/v1/accounts:signUp",
             body = RequestBody.create(json, JsonObject(mapOf("returnSecureToken" to JsonPrimitive(true))).toString()),
@@ -302,10 +390,21 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
                 FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject, isAnonymous = true)
             }
         )
+=======
+        val source =
+            enqueueAuthPost(
+                url = "identitytoolkit.googleapis.com/v1/accounts:signUp",
+                body = RequestBody.create(json, JsonObject(mapOf("returnSecureToken" to JsonPrimitive(true))).toString()),
+                setResult = { responseBody ->
+                    FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject, isAnonymous = true)
+                },
+            )
+>>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
         return source.task
     }
 
     fun signInWithCustomToken(customToken: String): Task<AuthResult> {
+<<<<<<< HEAD
         val source = enqueueAuthPost(
             url = "www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken",
             body = RequestBody.create(
@@ -314,11 +413,90 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
             ),
             setResult = { responseBody ->
                 FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
+=======
+        val source =
+            enqueueAuthPost(
+                url = "www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken",
+                body =
+                    RequestBody.create(
+                        json,
+                        JsonObject(mapOf("token" to JsonPrimitive(customToken), "returnSecureToken" to JsonPrimitive(true))).toString(),
+                    ),
+                setResult = { responseBody ->
+                    FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
+                },
+            ).task.continueWith {
+                updateByGetAccountInfo()
+>>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
             }
+        return source.result
+    }
+
+    internal fun updateByGetAccountInfo(): Task<AuthResult> {
+        val source = TaskCompletionSource<AuthResult>()
+
+        val body =
+            RequestBody.create(
+                json,
+                JsonObject(mapOf("idToken" to JsonPrimitive(user?.idToken))).toString(),
+            )
+        val request =
+            Request
+                .Builder()
+                .url(urlFactory.buildUrl("www.googleapis.com/identitytoolkit/v3/relyingparty/getAccountInfo"))
+                .post(body)
+                .build()
+
+        client.newCall(request).enqueue(
+            object : Callback {
+                override fun onFailure(
+                    call: Call,
+                    e: IOException,
+                ) {
+                    source.setException(FirebaseException(e.toString(), e))
+                }
+
+                @Throws(IOException::class)
+                override fun onResponse(
+                    call: Call,
+                    response: Response,
+                ) {
+                    if (!response.isSuccessful) {
+                        source.setException(
+                            createAuthInvalidUserException("updateWithAccountInfo", request, response),
+                        )
+                    } else {
+                        val newBody =
+                            jsonParser
+                                .parseToJsonElement(
+                                    response.body()?.use { it.string() } ?: "",
+                                ).jsonObject
+
+                        user?.let { prev ->
+                            user =
+                                FirebaseUserImpl(
+                                    app = app,
+                                    isAnonymous = prev.isAnonymous,
+                                    uid = prev.uid,
+                                    idToken = prev.idToken,
+                                    refreshToken = prev.refreshToken,
+                                    expiresIn = prev.expiresIn,
+                                    createdAt = newBody["createdAt"]?.jsonPrimitive?.longOrNull ?: prev.createdAt,
+                                    email = newBody["email"]?.jsonPrimitive?.contentOrNull ?: prev.email,
+                                    photoUrl = newBody["photoUrl"]?.jsonPrimitive?.contentOrNull ?: prev.photoUrl,
+                                    displayName = newBody["displayName"]?.jsonPrimitive?.contentOrNull ?: prev.displayName,
+                                )
+                            source.setResult(AuthResult { user })
+                        }
+                        source.setResult(null)
+                    }
+                }
+            },
         )
         return source.task
     }
 
+<<<<<<< HEAD
     fun createUserWithEmailAndPassword(email: String, password: String): Task<AuthResult> {
         val source = enqueueAuthPost(
             url = "www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser",
@@ -329,16 +507,37 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
                         "email" to JsonPrimitive(email),
                         "password" to JsonPrimitive(password),
                         "returnSecureToken" to JsonPrimitive(true)
+=======
+    fun createUserWithEmailAndPassword(
+        email: String,
+        password: String,
+    ): Task<AuthResult> {
+        val source =
+            enqueueAuthPost(
+                url = "www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser",
+                body =
+                    RequestBody.create(
+                        json,
+                        JsonObject(
+                            mapOf(
+                                "email" to JsonPrimitive(email),
+                                "password" to JsonPrimitive(password),
+                                "returnSecureToken" to JsonPrimitive(true),
+                            ),
+                        ).toString(),
+                    ),
+                setResult = { responseBody ->
+                    FirebaseUserImpl(
+                        app = app,
+                        data = jsonParser.parseToJsonElement(responseBody).jsonObject,
+>>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
                     )
-                ).toString()
-            ),
-            setResult = { responseBody ->
-                FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
-            }
-        )
+                },
+            )
         return source.task
     }
 
+<<<<<<< HEAD
 
     fun signInWithEmailAndPassword(email: String, password: String): Task<AuthResult> {
         val source = enqueueAuthPost(
@@ -357,32 +556,70 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
                 FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
             }
         )
+=======
+    fun signInWithEmailAndPassword(
+        email: String,
+        password: String,
+    ): Task<AuthResult> {
+        val source =
+            enqueueAuthPost(
+                url = "www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword",
+                body =
+                    RequestBody.create(
+                        json,
+                        JsonObject(
+                            mapOf(
+                                "email" to JsonPrimitive(email),
+                                "password" to JsonPrimitive(password),
+                                "returnSecureToken" to JsonPrimitive(true),
+                            ),
+                        ).toString(),
+                    ),
+                setResult = { responseBody ->
+                    FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
+                },
+            )
+>>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
         return source.task
     }
 
     internal fun createAuthInvalidUserException(
         action: String,
         request: Request,
-        response: Response
+        response: Response,
     ): FirebaseAuthInvalidUserException {
         val body = response.body()!!.use { it.string() }
         val jsonObject = jsonParser.parseToJsonElement(body).jsonObject
 
         return FirebaseAuthInvalidUserException(
-            jsonObject["error"]?.jsonObject
-                ?.get("message")?.jsonPrimitive
+            jsonObject["error"]
+                ?.jsonObject
+                ?.get("message")
+                ?.jsonPrimitive
                 ?.contentOrNull
                 ?: "UNKNOWN_ERROR",
             "$action API returned an error, " +
                 "with url [${request.method()}] ${request.url()} ${request.body()} -- " +
+<<<<<<< HEAD
                 "response [${response.code()}] ${response.message()} $body"
+=======
+                "response [${response.code()}] ${response.message()} $body",
+>>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
         )
     }
 
     fun signOut() {
         // cancel token refresher
-        client.dispatcher().queuedCalls().find { it.request().tag() == REFRESH_TOKEN_TAG }?.cancel() ?: {
-            client.dispatcher().runningCalls().find { it.request().tag() == REFRESH_TOKEN_TAG }?.cancel()
+        client
+            .dispatcher()
+            .queuedCalls()
+            .find { it.request().tag() == REFRESH_TOKEN_TAG }
+            ?.cancel() ?: {
+            client
+                .dispatcher()
+                .runningCalls()
+                .find { it.request().tag() == REFRESH_TOKEN_TAG }
+                ?.cancel()
         }
         user = null
     }
@@ -402,7 +639,11 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
 
     private var refreshSource = TaskCompletionSource<FirebaseUserImpl>().apply { setException(Exception()) }
 
-    internal fun <T> refreshToken(user: FirebaseUserImpl, source: TaskCompletionSource<T>, map: (user: FirebaseUserImpl) -> T?) {
+    internal fun <T> refreshToken(
+        user: FirebaseUserImpl,
+        source: TaskCompletionSource<T>,
+        map: (user: FirebaseUserImpl) -> T?,
+    ) {
         refreshSource = refreshSource
             .takeUnless { it.task.isComplete }
             ?: enqueueRefreshTokenCall(user)
@@ -412,119 +653,220 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
 
     private fun enqueueRefreshTokenCall(user: FirebaseUserImpl): TaskCompletionSource<FirebaseUserImpl> {
         val source = TaskCompletionSource<FirebaseUserImpl>()
-        val body = RequestBody.create(
-            json,
-            JsonObject(
-                mapOf(
-                    "refresh_token" to JsonPrimitive(user.refreshToken),
-                    "grant_type" to JsonPrimitive("refresh_token")
-                )
-            ).toString()
-        )
-        val request = Request.Builder()
-            .url(urlFactory.buildUrl("securetoken.googleapis.com/v1/token"))
-            .post(body)
-            .tag(REFRESH_TOKEN_TAG)
-            .build()
-
-        client.newCall(request).enqueue(object : Callback {
-
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(e)
-            }
-
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                val responseBody = response.body()?.use { it.string() }
+        val body =
+            RequestBody.create(
+                json,
+                JsonObject(
+                    mapOf(
+                        "refresh_token" to JsonPrimitive(user.refreshToken),
+                        "grant_type" to JsonPrimitive("refresh_token"),
+                    ),
+                ).toString(),
+            )
+        val request =
+            Request
+                .Builder()
+                .url(urlFactory.buildUrl("securetoken.googleapis.com/v1/token"))
+                .post(body)
+                .tag(REFRESH_TOKEN_TAG)
+                .build()
+
+        client.newCall(request).enqueue(
+            object : Callback {
+                override fun onFailure(
+                    call: Call,
+                    e: IOException,
+                ) {
+                    source.setException(e)
+                }
 
-                if (!response.isSuccessful || responseBody == null) {
-                    signOutAndThrowInvalidUserException(responseBody.orEmpty(), "token API returned an error: $body")
-                } else {
-                    jsonParser.parseToJsonElement(responseBody).jsonObject.apply {
-                        val newUser = FirebaseUserImpl(app, this, user.isAnonymous, user.email)
-                        if (newUser.claims["aud"] != app.options.projectId) {
-                            signOutAndThrowInvalidUserException(
-                                newUser.claims.toString(),
-                                "Project ID's do not match ${newUser.claims["aud"]} != ${app.options.projectId}"
-                            )
-                        } else {
-                            this@FirebaseAuth.user = newUser
-                            source.setResult(newUser)
+                @Throws(IOException::class)
+                override fun onResponse(
+                    call: Call,
+                    response: Response,
+                ) {
+                    val responseBody = response.body()?.use { it.string() }
+
+                    if (!response.isSuccessful || responseBody == null) {
+                        signOutAndThrowInvalidUserException(responseBody.orEmpty(), "token API returned an error: $body")
+                    } else {
+                        jsonParser.parseToJsonElement(responseBody).jsonObject.apply {
+                            val newUser = FirebaseUserImpl(app, this, user.isAnonymous, user.email)
+                            if (newUser.claims["aud"] != app.options.projectId) {
+                                signOutAndThrowInvalidUserException(
+                                    newUser.claims.toString(),
+                                    "Project ID's do not match ${newUser.claims["aud"]} != ${app.options.projectId}",
+                                )
+                            } else {
+                                this@FirebaseAuth.user = newUser
+                                source.setResult(newUser)
+                            }
                         }
                     }
                 }
-            }
 
-            private fun signOutAndThrowInvalidUserException(body: String, message: String) {
-                signOut()
-                source.setException(FirebaseAuthInvalidUserException(body, message))
-            }
-        })
+                private fun signOutAndThrowInvalidUserException(
+                    body: String,
+                    message: String,
+                ) {
+                    signOut()
+                    source.setException(FirebaseAuthInvalidUserException(body, message))
+                }
+            },
+        )
         return source
     }
 
     internal fun updateEmail(email: String): Task<Unit> {
         val source = TaskCompletionSource<Unit>()
 
-        val body = RequestBody.create(
-            json,
-            JsonObject(
-                mapOf(
-                    "idToken" to JsonPrimitive(user?.idToken),
-                    "email" to JsonPrimitive(email),
-                    "returnSecureToken" to JsonPrimitive(true)
-                )
-            ).toString()
+        val body =
+            RequestBody.create(
+                json,
+                JsonObject(
+                    mapOf(
+                        "idToken" to JsonPrimitive(user?.idToken),
+                        "email" to JsonPrimitive(email),
+                        "returnSecureToken" to JsonPrimitive(true),
+                    ),
+                ).toString(),
+            )
+        val request =
+            Request
+                .Builder()
+                .url(urlFactory.buildUrl("identitytoolkit.googleapis.com/v1/accounts:update"))
+                .post(body)
+                .build()
+
+        client.newCall(request).enqueue(
+            object : Callback {
+                override fun onFailure(
+                    call: Call,
+                    e: IOException,
+                ) {
+                    source.setException(FirebaseException(e.toString(), e))
+                }
+
+                @Throws(IOException::class)
+                override fun onResponse(
+                    call: Call,
+                    response: Response,
+                ) {
+                    if (!response.isSuccessful) {
+                        signOut()
+                        source.setException(
+                            createAuthInvalidUserException(
+                                "updateEmail",
+                                request,
+                                response,
+                            ),
+                        )
+                    } else {
+                        val newBody =
+                            jsonParser
+                                .parseToJsonElement(
+                                    response.body()?.use { it.string() } ?: "",
+                                ).jsonObject
+
+                        user?.let { prev ->
+                            user =
+                                FirebaseUserImpl(
+                                    app = app,
+                                    isAnonymous = prev.isAnonymous,
+                                    uid = prev.uid,
+                                    idToken = newBody["idToken"]?.jsonPrimitive?.contentOrNull ?: prev.idToken,
+                                    refreshToken = newBody["refreshToken"]?.jsonPrimitive?.contentOrNull ?: prev.refreshToken,
+                                    expiresIn = newBody["expiresIn"]?.jsonPrimitive?.intOrNull ?: prev.expiresIn,
+                                    createdAt = prev.createdAt,
+                                    email = newBody["newEmail"]?.jsonPrimitive?.contentOrNull ?: prev.email,
+                                    photoUrl = newBody["photoUrl"]?.jsonPrimitive?.contentOrNull ?: prev.photoUrl,
+                                    displayName = newBody["displayName"]?.jsonPrimitive?.contentOrNull ?: prev.displayName,
+                                )
+                        }
+                        source.setResult(null)
+                    }
+                }
+            },
         )
-        val request = Request.Builder()
-            .url("https://identitytoolkit.googleapis.com/v1/accounts:update?key=" + app.options.apiKey)
-            .post(body)
-            .build()
+        return source.task
+    }
 
-        client.newCall(request).enqueue(object : Callback {
+    internal fun updateProfile(request: UserProfileChangeRequest): Task<Unit> {
+        val source = TaskCompletionSource<Unit>()
 
-            override fun onFailure(call: Call, e: IOException) {
-                source.setException(FirebaseException(e.toString(), e))
-            }
+        val body =
+            RequestBody.create(
+                json,
+                JsonObject(
+                    mapOf(
+                        "idToken" to JsonPrimitive(user?.idToken),
+                        "displayName" to JsonPrimitive(request.displayName),
+                        "photoUrl" to JsonPrimitive(request.photoUrl),
+                        "returnSecureToken" to JsonPrimitive(true),
+                    ),
+                ).toString(),
+            )
+        val req =
+            Request
+                .Builder()
+                .url(urlFactory.buildUrl("identitytoolkit.googleapis.com/v1/accounts:update"))
+                .post(body)
+                .build()
+
+        client.newCall(req).enqueue(
+            object : Callback {
+                override fun onFailure(
+                    call: Call,
+                    e: IOException,
+                ) {
+                    source.setException(FirebaseException(e.toString(), e))
+                }
 
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    signOut()
-                    source.setException(
-                        createAuthInvalidUserException(
-                            "updateEmail",
-                            request,
-                            response
-                        )
-                    )
-                } else {
-                    val newBody = jsonParser.parseToJsonElement(
-                        response.body()?.use { it.string() } ?: ""
-                    ).jsonObject
-
-                    user?.let { prev ->
-                        user = FirebaseUserImpl(
-                            app = app,
-                            isAnonymous = prev.isAnonymous,
-                            uid = prev.uid,
-                            idToken = newBody["idToken"]?.jsonPrimitive?.contentOrNull ?: prev.idToken,
-                            refreshToken = newBody["refreshToken"]?.jsonPrimitive?.contentOrNull ?: prev.refreshToken,
-                            expiresIn = newBody["expiresIn"]?.jsonPrimitive?.intOrNull ?: prev.expiresIn,
-                            createdAt = prev.createdAt,
-                            email = newBody["newEmail"]?.jsonPrimitive?.contentOrNull ?: prev.email
+                @Throws(IOException::class)
+                override fun onResponse(
+                    call: Call,
+                    response: Response,
+                ) {
+                    if (!response.isSuccessful) {
+                        signOut()
+                        source.setException(
+                            createAuthInvalidUserException(
+                                "updateProfile",
+                                req,
+                                response,
+                            ),
                         )
+                    } else {
+                        val newBody =
+                            jsonParser
+                                .parseToJsonElement(
+                                    response.body()?.use { it.string() } ?: "",
+                                ).jsonObject
+
+                        user?.let { prev ->
+                            user =
+                                FirebaseUserImpl(
+                                    app = app,
+                                    isAnonymous = prev.isAnonymous,
+                                    uid = prev.uid,
+                                    idToken = newBody["idToken"]?.jsonPrimitive?.contentOrNull ?: prev.idToken,
+                                    refreshToken = newBody["refreshToken"]?.jsonPrimitive?.contentOrNull ?: prev.refreshToken,
+                                    expiresIn = newBody["expiresIn"]?.jsonPrimitive?.intOrNull ?: prev.expiresIn,
+                                    createdAt = prev.createdAt,
+                                    email = newBody["newEmail"]?.jsonPrimitive?.contentOrNull ?: prev.email,
+                                    photoUrl = newBody["photoUrl"]?.jsonPrimitive?.contentOrNull ?: prev.photoUrl,
+                                    displayName = newBody["displayName"]?.jsonPrimitive?.contentOrNull ?: prev.displayName,
+                                )
+                        }
+                        source.setResult(null)
                     }
-                    source.setResult(null)
                 }
-            }
-        })
+            },
+        )
         return source.task
     }
 
-    override fun getUid(): String? {
-        return user?.uid
-    }
+    override fun getUid(): String? = user?.uid
 
     override fun addIdTokenListener(listener: com.google.firebase.auth.internal.IdTokenListener) {
         internalIdTokenListeners.addIfAbsent(listener)
@@ -571,22 +913,58 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider {
         idTokenListeners.remove(listener)
     }
 
+<<<<<<< HEAD
     fun useEmulator(host: String, port: Int) {
         urlFactory = UrlFactory(app, "http://$host:$port/")
     }
 
     fun sendPasswordResetEmail(email: String, settings: ActionCodeSettings?): Task<Unit> = TODO()
     fun signInWithCredential(authCredential: AuthCredential): Task<AuthResult> = TODO()
+=======
+    fun sendPasswordResetEmail(
+        email: String,
+        settings: ActionCodeSettings?,
+    ): Task<Unit> = TODO()
+
+>>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
     fun checkActionCode(code: String): Task<ActionCodeResult> = TODO()
-    fun confirmPasswordReset(code: String, newPassword: String): Task<Unit> = TODO()
+
+    fun confirmPasswordReset(
+        code: String,
+        newPassword: String,
+    ): Task<Unit> = TODO()
+
     fun fetchSignInMethodsForEmail(email: String): Task<SignInMethodQueryResult> = TODO()
-    fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings): Task<Unit> = TODO()
+
+    fun sendSignInLinkToEmail(
+        email: String,
+        actionCodeSettings: ActionCodeSettings,
+    ): Task<Unit> = TODO()
+
     fun verifyPasswordResetCode(code: String): Task<String> = TODO()
+
     fun updateCurrentUser(user: FirebaseUser): Task<Unit> = TODO()
+
     fun applyActionCode(code: String): Task<Unit> = TODO()
+
     val languageCode: String get() = TODO()
+
     fun isSignInWithEmailLink(link: String): Boolean = TODO()
-    fun signInWithEmailLink(email: String, link: String): Task<AuthResult> = TODO()
+
+    fun signInWithEmailLink(
+        email: String,
+        link: String,
+    ): Task<AuthResult> = TODO()
 
     fun setLanguageCode(value: String): Nothing = TODO()
+<<<<<<< HEAD
+=======
+
+    fun useEmulator(
+        host: String,
+        port: Int,
+    ) {
+        urlFactory = UrlFactory(app, "http://$host:$port/")
+    }
+>>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
 }
diff --git a/src/main/java/com/google/firebase/auth/FirebaseUser.kt b/src/main/java/com/google/firebase/auth/FirebaseUser.kt
index 50e4e52..0932518 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseUser.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseUser.kt
@@ -5,28 +5,45 @@ import com.google.android.gms.tasks.Task
 abstract class FirebaseUser {
     abstract val uid: String
     abstract val email: String?
+    abstract val photoUrl: String?
+    abstract val displayName: String?
     abstract val isAnonymous: Boolean
+
     abstract fun delete(): Task<Void>
+
     abstract fun reload(): Task<Void>
-    abstract fun verifyBeforeUpdateEmail(newEmail: String, actionCodeSettings: ActionCodeSettings?): Task<Unit>
+
+    abstract fun verifyBeforeUpdateEmail(
+        newEmail: String,
+        actionCodeSettings: ActionCodeSettings?,
+    ): Task<Unit>
+
     abstract fun updateEmail(email: String): Task<Unit>
 
-    val displayName: String get() = TODO()
+    abstract fun getIdToken(forceRefresh: Boolean): Task<GetTokenResult>
+
+    abstract fun updateProfile(request: UserProfileChangeRequest): Task<Unit>
+
     val phoneNumber: String get() = TODO()
-    val photoUrl: String? get() = TODO()
     val isEmailVerified: Boolean get() = TODO()
     val metadata: FirebaseUserMetadata get() = TODO()
     val multiFactor: MultiFactor get() = TODO()
     val providerData: List<UserInfo> get() = TODO()
     val providerId: String get() = TODO()
-    abstract fun getIdToken(forceRefresh: Boolean): Task<GetTokenResult>
+
     fun linkWithCredential(credential: AuthCredential): Task<AuthResult> = TODO()
+
     fun sendEmailVerification(): Task<Unit> = TODO()
+
     fun sendEmailVerification(actionCodeSettings: ActionCodeSettings): Task<Unit> = TODO()
+
     fun unlink(provider: String): Task<AuthResult> = TODO()
+
     fun updatePassword(password: String): Task<Unit> = TODO()
+
     fun updatePhoneNumber(credential: AuthCredential): Task<Unit> = TODO()
-    fun updateProfile(request: UserProfileChangeRequest): Task<Unit> = TODO()
+
     fun reauthenticate(credential: AuthCredential): Task<Unit> = TODO()
+
     fun reauthenticateAndRetrieveData(credential: AuthCredential): Task<AuthResult> = TODO()
 }
diff --git a/src/main/java/com/google/firebase/auth/OobRequestType.kt b/src/main/java/com/google/firebase/auth/OobRequestType.kt
index f0c31c3..51a77e2 100644
--- a/src/main/java/com/google/firebase/auth/OobRequestType.kt
+++ b/src/main/java/com/google/firebase/auth/OobRequestType.kt
@@ -5,4 +5,4 @@ internal enum class OobRequestType {
     EMAIL_SIGNIN,
     VERIFY_EMAIL,
     VERIFY_AND_CHANGE_EMAIL
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.java b/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.java
deleted file mode 100644
index 437f98d..0000000
--- a/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.google.firebase.auth;
-
-import android.net.Uri;
-import kotlin.NotImplementedError;
-
-public class UserProfileChangeRequest {
-    public static class Builder {
-        public Builder setDisplayName(String name) {
-            throw new NotImplementedError();
-        }
-        public Builder setPhotoUri(Uri uri) {
-            throw new NotImplementedError();
-        }
-        public UserProfileChangeRequest build() {
-            throw new NotImplementedError();
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.kt b/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.kt
new file mode 100644
index 0000000..c60bb83
--- /dev/null
+++ b/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.kt
@@ -0,0 +1,47 @@
+package com.google.firebase.auth
+
+import android.net.Uri
+import android.os.Parcel
+import android.os.Parcelable
+
+class UserProfileChangeRequest private constructor(
+    internal val displayName: String?,
+    internal val photoUrl: String?,
+) : Parcelable {
+    override fun describeContents(): Int = displayName.hashCode() + photoUrl.hashCode()
+
+    override fun writeToParcel(
+        dest: Parcel,
+        flags: Int,
+    ) {
+        dest.writeString(displayName)
+        dest.writeString(photoUrl)
+    }
+
+    internal companion object CREATOR : Parcelable.Creator<UserProfileChangeRequest> {
+        override fun createFromParcel(parcel: Parcel): UserProfileChangeRequest {
+            val displayName = parcel.readString()
+            val photoUri = parcel.readString()
+            return UserProfileChangeRequest(displayName, photoUri)
+        }
+
+        override fun newArray(size: Int): Array<UserProfileChangeRequest?> = arrayOfNulls(size)
+    }
+
+    class Builder {
+        private var displayName: String? = null
+        private var photoUri: Uri? = null
+
+        fun setDisplayName(name: String?): Builder {
+            this.displayName = name
+            return this
+        }
+
+        fun setPhotoUri(uri: Uri?): Builder {
+            this.photoUri = uri
+            return this
+        }
+
+        fun build(): UserProfileChangeRequest = UserProfileChangeRequest(displayName, photoUri?.toString())
+    }
+}
diff --git a/src/test/kotlin/AppTest.kt b/src/test/kotlin/AppTest.kt
index d017e38..2e5697f 100644
--- a/src/test/kotlin/AppTest.kt
+++ b/src/test/kotlin/AppTest.kt
@@ -8,20 +8,34 @@ import org.junit.Test
 class AppTest : FirebaseTest() {
     @Test
     fun testInitialize() {
-        FirebasePlatform.initializeFirebasePlatform(object : FirebasePlatform() {
-            val storage = mutableMapOf<String, String>()
-            override fun store(key: String, value: String) = storage.set(key, value)
-            override fun retrieve(key: String) = storage[key]
-            override fun clear(key: String) { storage.remove(key) }
-            override fun log(msg: String) = println(msg)
-        })
-        val options = FirebaseOptions.Builder()
-            .setProjectId("my-firebase-project")
-            .setApplicationId("1:27992087142:android:ce3b6448250083d1")
-            .setApiKey("AIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
-            // setDatabaseURL(...)
-            // setStorageBucket(...)
-            .build()
+        FirebasePlatform.initializeFirebasePlatform(
+            object : FirebasePlatform() {
+                val storage = mutableMapOf<String, String>()
+
+                override fun store(
+                    key: String,
+                    value: String,
+                ) = storage.set(key, value)
+
+                override fun retrieve(key: String) = storage[key]
+
+                override fun clear(key: String) {
+                    storage.remove(key)
+                }
+
+                override fun log(msg: String) = println(msg)
+            },
+        )
+        val options =
+            FirebaseOptions
+                .Builder()
+                .setProjectId("fir-java-sdk")
+                .setApplicationId("1:341458593155:web:bf8e1aa37efe01f32d42b6")
+                .setApiKey("AIzaSyCvVHjTJHyeStnzIE7J9LLtHqWk6reGM08")
+                .setDatabaseUrl("https://fir-java-sdk-default-rtdb.firebaseio.com")
+                .setStorageBucket("fir-java-sdk.appspot.com")
+                .setGcmSenderId("341458593155")
+                .build()
         val app = Firebase.initialize(Application(), options)
     }
 }
diff --git a/src/test/kotlin/AuthTest.kt b/src/test/kotlin/AuthTest.kt
index 7bdb90c..080c784 100644
--- a/src/test/kotlin/AuthTest.kt
+++ b/src/test/kotlin/AuthTest.kt
@@ -1,47 +1,97 @@
-import com.google.firebase.auth.FirebaseAuth
+import android.net.Uri
 import com.google.firebase.auth.FirebaseAuthInvalidUserException
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.tasks.await
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
 import org.junit.Assert.assertThrows
+import org.junit.Before
 import org.junit.Test
+import java.util.UUID
 
 class AuthTest : FirebaseTest() {
-    private fun createAuth(): FirebaseAuth {
-        return FirebaseAuth(app).apply {
+    private val email = "email${UUID.randomUUID()}@example.com"
+
+    @Before
+    fun initialize() {
+        auth.apply {
             useEmulator("localhost", 9099)
         }
     }
 
     @Test
-    fun `should authenticate via anonymous auth`() = runTest {
-        val auth = createAuth()
+    fun `should authenticate via anonymous auth`() =
+        runTest {
+            auth.signInAnonymously().await()
 
-        auth.signInAnonymously().await()
+            assertEquals(true, auth.currentUser?.isAnonymous)
+        }
 
-        assertEquals(true, auth.currentUser?.isAnonymous)
-    }
+    @Test
+    fun `should create user via email and password`() =
+        runTest {
+            val createResult = auth.createUserWithEmailAndPassword(email, "test123").await()
+            assertNotEquals(null, createResult.user?.uid)
+            assertEquals(null, createResult.user?.displayName)
+            // assertEquals(null, createResult.user?.phoneNumber)
+            assertEquals(false, createResult.user?.isAnonymous)
+            assertEquals(email, createResult.user?.email)
+            assertNotEquals("", createResult.user!!.email)
+
+            val signInResult = auth.signInWithEmailAndPassword(email, "test123").await()
+            assertEquals(createResult.user?.uid, signInResult.user?.uid)
+        }
 
     @Test
-    fun `should authenticate via email and password`() = runTest {
-        val auth = createAuth()
+    fun `should authenticate via email and password`() =
+        runTest {
+            auth.createUserWithEmailAndPassword(email, "test123").await()
 
-        auth.signInWithEmailAndPassword("email@example.com", "securepassword").await()
+            auth.signInWithEmailAndPassword(email, "test123").await()
 
-        assertEquals(false, auth.currentUser?.isAnonymous)
-    }
+            assertEquals(false, auth.currentUser?.isAnonymous)
+        }
 
     @Test
-    fun `should throw exception on invalid password`() {
-        val auth = createAuth()
+    fun `should update displayName and photoUrl`() =
+        runTest {
+            auth
+                .createUserWithEmailAndPassword(email, "test123")
+                .await()
+                .user
+            auth.currentUser
+                ?.updateProfile(
+                    com.google.firebase.auth.UserProfileChangeRequest
+                        .Builder()
+                        .setDisplayName("testDisplayName")
+                        .setPhotoUri(Uri.parse("https://picsum.photos/100"))
+                        .build(),
+                )?.await()
+            assertEquals("testDisplayName", auth.currentUser?.displayName)
+            assertEquals("https://picsum.photos/100", auth.currentUser?.photoUrl)
+        }
 
-        val exception = assertThrows(FirebaseAuthInvalidUserException::class.java) {
-            runBlocking {
-                auth.signInWithEmailAndPassword("email@example.com", "wrongpassword").await()
-            }
+    @Test
+    fun `should sign in anonymously`() =
+        runTest {
+            val signInResult = auth.signInAnonymously().await()
+            assertNotEquals("", signInResult.user!!.email)
+            assertEquals(true, signInResult.user?.isAnonymous)
         }
 
-        assertEquals("INVALID_PASSWORD", exception.errorCode)
-    }
+    @Test
+    fun `should throw exception on invalid password`() =
+        runTest {
+            auth.createUserWithEmailAndPassword(email, "test123").await()
+
+            val exception =
+                assertThrows(FirebaseAuthInvalidUserException::class.java) {
+                    runBlocking {
+                        auth.signInWithEmailAndPassword(email, "wrongpassword").await()
+                    }
+                }
+
+            assertEquals("INVALID_PASSWORD", exception.errorCode)
+        }
 }
diff --git a/src/test/kotlin/FirebaseAuthTest.kt b/src/test/kotlin/FirebaseAuthTest.kt
index 029cd11..b2000f2 100644
--- a/src/test/kotlin/FirebaseAuthTest.kt
+++ b/src/test/kotlin/FirebaseAuthTest.kt
@@ -1,71 +1,57 @@
 
-import android.app.Application
-import com.google.firebase.Firebase
-import com.google.firebase.FirebaseOptions
-import com.google.firebase.FirebasePlatform
-import com.google.firebase.auth.FirebaseAuth
-import com.google.firebase.initialize
+import android.net.Uri
+import com.google.firebase.auth.FirebaseUser
 import kotlinx.coroutines.tasks.await
 import kotlinx.coroutines.test.runTest
-import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
-import org.junit.Before
 import org.junit.Test
-import java.io.File
 import java.util.UUID
 
 internal class FirebaseAuthTest : FirebaseTest() {
-
-    private lateinit var auth: FirebaseAuth
-
-    @Before
-    fun initialize() {
-        FirebasePlatform.initializeFirebasePlatform(object : FirebasePlatform() {
-            val storage = mutableMapOf<String, String>()
-            override fun store(key: String, value: String) = storage.set(key, value)
-            override fun retrieve(key: String) = storage[key]
-            override fun clear(key: String) { storage.remove(key) }
-            override fun log(msg: String) = println(msg)
-            override fun getDatabasePath(name: String) = File("./build/$name")
-        })
-        val options = FirebaseOptions.Builder()
-            .setProjectId("fir-java-sdk")
-            .setApplicationId("1:341458593155:web:bf8e1aa37efe01f32d42b6")
-            .setApiKey("AIzaSyCvVHjTJHyeStnzIE7J9LLtHqWk6reGM08")
-            .setDatabaseUrl("https://fir-java-sdk-default-rtdb.firebaseio.com")
-            .setStorageBucket("fir-java-sdk.appspot.com")
-            .setGcmSenderId("341458593155")
-            .build()
-
-        val firebaseApp = Firebase.initialize(Application(), options)
-        auth = FirebaseAuth.getInstance(app = firebaseApp)
-    }
-
-    @After
-    fun clear() {
-        auth.currentUser?.delete()
-    }
-
     @Test
-    fun testCreateUserWithEmailAndPassword() = runTest {
-        val email = "test+${UUID.randomUUID()}@test.com"
-        val createResult = auth.createUserWithEmailAndPassword(email, "test123").await()
-        assertNotEquals(null, createResult.user?.uid)
-        // assertEquals(null, createResult.user?.displayName)
-        // assertEquals(null, createResult.user?.phoneNumber)
-        assertEquals(false, createResult.user?.isAnonymous)
-        assertEquals(email, createResult.user?.email)
-        assertNotEquals("", createResult.user!!.email)
+    fun testCreateUserWithEmailAndPassword() =
+        runTest {
+            val email = "test+${UUID.randomUUID()}@test.com"
+            val createResult = auth.createUserWithEmailAndPassword(email, "test123").await()
+            assertNotEquals(null, createResult.user?.uid)
+            // assertEquals(null, createResult.user?.displayName)
+            // assertEquals(null, createResult.user?.phoneNumber)
+            assertEquals(false, createResult.user?.isAnonymous)
+            assertEquals(email, createResult.user?.email)
+            assertNotEquals("", createResult.user!!.email)
+
+            val signInResult = auth.signInWithEmailAndPassword(email, "test123").await()
+            assertEquals(createResult.user?.uid, signInResult.user?.uid)
+        }
 
-        val signInResult = auth.signInWithEmailAndPassword(email, "test123").await()
-        assertEquals(createResult.user?.uid, signInResult.user?.uid)
-    }
+    @Test
+    fun testUpdateProfile() =
+        runTest {
+            val user = createUser()
+            user
+                ?.updateProfile(
+                    com.google.firebase.auth.UserProfileChangeRequest
+                        .Builder()
+                        .setDisplayName("testDisplayName")
+                        .setPhotoUri(Uri.parse("https://picsum.photos/100"))
+                        .build(),
+                )?.await()
+            assertEquals("testDisplayName", auth.currentUser?.displayName)
+            assertEquals("https://picsum.photos/100", auth.currentUser?.photoUrl)
+        }
 
     @Test
-    fun testSignInAnonymously() = runTest {
-        val signInResult = auth.signInAnonymously().await()
-        assertNotEquals("", signInResult.user!!.email)
-        assertEquals(true, signInResult.user?.isAnonymous)
-    }
+    fun testSignInAnonymously() =
+        runTest {
+            val signInResult = auth.signInAnonymously().await()
+            assertNotEquals("", signInResult.user!!.email)
+            assertEquals(true, signInResult.user?.isAnonymous)
+        }
+
+    private suspend fun createUser(email: String = "test+${UUID.randomUUID()}@test.com"): FirebaseUser? =
+        auth
+            .createUserWithEmailAndPassword(email, "test123")
+            .await()
+            .user
 }
diff --git a/src/test/kotlin/FirebaseTest.kt b/src/test/kotlin/FirebaseTest.kt
index 77aa858..a714f8c 100644
--- a/src/test/kotlin/FirebaseTest.kt
+++ b/src/test/kotlin/FirebaseTest.kt
@@ -3,31 +3,72 @@ import com.google.firebase.Firebase
 import com.google.firebase.FirebaseApp
 import com.google.firebase.FirebaseOptions
 import com.google.firebase.FirebasePlatform
+import com.google.firebase.auth.FirebaseAuth
 import com.google.firebase.initialize
+import com.google.firebase.ktx.initialize
+import org.junit.After
 import org.junit.Before
 import java.io.File
 
 abstract class FirebaseTest {
+    protected lateinit var auth: FirebaseAuth
+
     protected val app: FirebaseApp get() {
-        val options = FirebaseOptions.Builder()
-            .setProjectId("my-firebase-project")
-            .setApplicationId("1:27992087142:android:ce3b6448250083d1")
-            .setApiKey("AIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
-            .build()
+        val options =
+            FirebaseOptions
+                .Builder()
+                .setProjectId("fir-java-sdk")
+                .setApplicationId("1:341458593155:web:bf8e1aa37efe01f32d42b6")
+                .setApiKey("AIzaSyCvVHjTJHyeStnzIE7J9LLtHqWk6reGM08")
+                .setDatabaseUrl("https://fir-java-sdk-default-rtdb.firebaseio.com")
+                .setStorageBucket("fir-java-sdk.appspot.com")
+                .setGcmSenderId("341458593155")
+                .build()
 
         return Firebase.initialize(Application(), options)
     }
 
     @Before
     fun beforeEach() {
-        FirebasePlatform.initializeFirebasePlatform(object : FirebasePlatform() {
-            val storage = mutableMapOf<String, String>()
-            override fun store(key: String, value: String) = storage.set(key, value)
-            override fun retrieve(key: String) = storage[key]
-            override fun clear(key: String) { storage.remove(key) }
-            override fun log(msg: String) = println(msg)
-            override fun getDatabasePath(name: String) = File("./build/$name")
-        })
+        FirebasePlatform.initializeFirebasePlatform(
+            object : FirebasePlatform() {
+                val storage = mutableMapOf<String, String>()
+
+                override fun store(
+                    key: String,
+                    value: String,
+                ) = storage.set(key, value)
+
+                override fun retrieve(key: String) = storage[key]
+
+                override fun clear(key: String) {
+                    storage.remove(key)
+                }
+
+                override fun log(msg: String) = println(msg)
+
+                override fun getDatabasePath(name: String) = File("./build/$name")
+            },
+        )
+        val options =
+            FirebaseOptions
+                .Builder()
+                .setProjectId("fir-java-sdk")
+                .setApplicationId("1:341458593155:web:bf8e1aa37efe01f32d42b6")
+                .setApiKey("AIzaSyCvVHjTJHyeStnzIE7J9LLtHqWk6reGM08")
+                .setDatabaseUrl("https://fir-java-sdk-default-rtdb.firebaseio.com")
+                .setStorageBucket("fir-java-sdk.appspot.com")
+                .setGcmSenderId("341458593155")
+                .build()
+
+        val firebaseApp = Firebase.initialize(Application(), options)
+        auth = FirebaseAuth.getInstance(app = firebaseApp)
+
         FirebaseApp.clearInstancesForTest()
     }
+
+    @After
+    fun clear() {
+        auth.currentUser?.delete()
+    }
 }

From 9465f7ac54ae8b78a7adc3f236a0c0cb63a6b37f Mon Sep 17 00:00:00 2001
From: Jakub Kostka <jacob.cube.mexx@gmail.com>
Date: Thu, 7 Nov 2024 16:38:29 +0100
Subject: [PATCH 19/23] refactor

---
 .../java/com/google/firebase/auth/FirebaseAuth.kt |  4 ++--
 src/test/kotlin/AuthTest.kt                       | 15 +++++++++++++++
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index 11e2db4..3adbe1b 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -425,11 +425,11 @@ class FirebaseAuth constructor(
                 setResult = { responseBody ->
                     FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
                 },
-            ).task.continueWith {
+            ).task.addOnSuccessListener {
                 updateByGetAccountInfo()
 >>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
             }
-        return source.result
+        return source
     }
 
     internal fun updateByGetAccountInfo(): Task<AuthResult> {
diff --git a/src/test/kotlin/AuthTest.kt b/src/test/kotlin/AuthTest.kt
index 080c784..d603d5f 100644
--- a/src/test/kotlin/AuthTest.kt
+++ b/src/test/kotlin/AuthTest.kt
@@ -53,6 +53,21 @@ class AuthTest : FirebaseTest() {
             assertEquals(false, auth.currentUser?.isAnonymous)
         }
 
+    /*@Test
+    fun `should authenticate via custom token`() =
+        runTest {
+            val user = auth.createUserWithEmailAndPassword(email, "test123").await()
+            auth
+                .signInWithCustomToken(
+                    user.user
+                        .getIdToken(false)
+                        .await()
+                        .token ?: "",
+                ).await()
+
+            assertEquals(false, auth.currentUser?.isAnonymous)
+        }*/
+
     @Test
     fun `should update displayName and photoUrl`() =
         runTest {

From 372e307c976e11d93ffdf27925868850b0114731 Mon Sep 17 00:00:00 2001
From: Nicholas Bransby-Williams <nbransby@gmail.com>
Date: Thu, 28 Nov 2024 13:57:22 +0000
Subject: [PATCH 20/23] Update gradle.properties

---
 gradle.properties | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gradle.properties b/gradle.properties
index 3eecc3f..195f965 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1 +1 @@
-version=0.4.6
+version=0.4.7

From 740c5b4d3b17e2d58c48a6d24af50c74fc25045c Mon Sep 17 00:00:00 2001
From: Nicholas Bransby-Williams <nbransby@gmail.com>
Date: Thu, 28 Nov 2024 13:58:01 +0000
Subject: [PATCH 21/23] Update README.md

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 27cc516..842c786 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@ You can add the library via Gradle:
 
 ```kotlin
 dependencies {
-    implementation("dev.gitlive:firebase-java-sdk:0.4.6")
+    implementation("dev.gitlive:firebase-java-sdk:0.4.7")
 }
 ```
 
@@ -35,7 +35,7 @@ Or Maven:
 <dependency>
     <groupId>dev.gitlive</groupId>
     <artifactId>firebase-java-sdk</artifactId>
-    <version>0.4.6</version>
+    <version>0.4.7</version>
 </dependency>
 ```
 

From 6ee896e13aac6ce907cc2b3e5121a45bb5f0f945 Mon Sep 17 00:00:00 2001
From: jacob <jakub.kostka@augmy.org>
Date: Wed, 22 Jan 2025 09:23:45 +0100
Subject: [PATCH 22/23] Master merge

---
 .../com/google/firebase/auth/FirebaseAuth.kt  | 134 ++----------------
 src/test/kotlin/AuthTest.kt                   |   2 +-
 2 files changed, 10 insertions(+), 126 deletions(-)

diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index 3adbe1b..5198027 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -96,19 +96,10 @@ class FirebaseUserImpl internal constructor(
         jsonParser
             .parseToJsonElement(String(Base64.getUrlDecoder().decode(idToken.split(".")[1])))
             .jsonObject
-            .run { value as Map<String, Any?>? }
+            .run { value as? Map<String, Any?>? }
             .orEmpty()
     }
 
-<<<<<<< HEAD
-    val JsonElement.value get(): Any? = when (this) {
-        is JsonNull -> null
-        is JsonArray -> map { it.value }
-        is JsonObject -> jsonObject.mapValues { (_, it) -> it.value }
-        is JsonPrimitive -> booleanOrNull ?: doubleOrNull ?: content
-        else -> TODO()
-    }
-=======
     internal val JsonElement.value get(): Any? =
         when (this) {
             is JsonNull -> null
@@ -117,7 +108,6 @@ class FirebaseUserImpl internal constructor(
             is JsonPrimitive -> booleanOrNull ?: doubleOrNull ?: content
             else -> TODO()
         }
->>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
 
     override fun delete(): Task<Void> {
         val source = TaskCompletionSource<Void>()
@@ -261,19 +251,10 @@ class FirebaseAuth constructor(
         setResult: (responseBody: String) -> FirebaseUserImpl?,
     ): TaskCompletionSource<AuthResult> {
         val source = TaskCompletionSource<AuthResult>()
-<<<<<<< HEAD
         val request = Request.Builder()
             .url(urlFactory.buildUrl(url))
             .post(body)
             .build()
-=======
-        val request =
-            Request
-                .Builder()
-                .url(urlFactory.buildUrl(url))
-                .post(body)
-                .build()
->>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
 
         client.newCall(request).enqueue(
             object : Callback {
@@ -284,26 +265,12 @@ class FirebaseAuth constructor(
                     source.setException(FirebaseException(e.toString(), e))
                 }
 
-<<<<<<< HEAD
-            @Throws(IOException::class)
-            override fun onResponse(call: Call, response: Response) {
-                if (!response.isSuccessful) {
-                    source.setException(
-                        createAuthInvalidUserException("accounts", request, response)
-                    )
-                } else {
-                    if(response.body()?.use { it.string() }?.also { responseBody ->
-                        user = setResult(responseBody)
-                        source.setResult(AuthResult { user })
-                    } == null) {
-=======
                 @Throws(IOException::class)
                 override fun onResponse(
                     call: Call,
                     response: Response,
                 ) {
                     if (!response.isSuccessful) {
->>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
                         source.setException(
                             createAuthInvalidUserException("accounts", request, response),
                         )
@@ -325,9 +292,6 @@ class FirebaseAuth constructor(
     }
 
     companion object {
-        @JvmStatic
-        fun getInstance(): FirebaseAuth = getInstance(FirebaseApp.getInstance())
-
         @JvmStatic
         fun getInstance(app: FirebaseApp): FirebaseAuth = app.get(FirebaseAuth::class.java)
 
@@ -382,57 +346,33 @@ class FirebaseAuth constructor(
     private var urlFactory = UrlFactory(app)
 
     fun signInAnonymously(): Task<AuthResult> {
-<<<<<<< HEAD
         val source = enqueueAuthPost(
             url = "identitytoolkit.googleapis.com/v1/accounts:signUp",
             body = RequestBody.create(json, JsonObject(mapOf("returnSecureToken" to JsonPrimitive(true))).toString()),
             setResult = { responseBody ->
                 FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject, isAnonymous = true)
-            }
+            },
         )
-=======
-        val source =
-            enqueueAuthPost(
-                url = "identitytoolkit.googleapis.com/v1/accounts:signUp",
-                body = RequestBody.create(json, JsonObject(mapOf("returnSecureToken" to JsonPrimitive(true))).toString()),
-                setResult = { responseBody ->
-                    FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject, isAnonymous = true)
-                },
-            )
->>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
         return source.task
     }
 
     fun signInWithCustomToken(customToken: String): Task<AuthResult> {
-<<<<<<< HEAD
         val source = enqueueAuthPost(
             url = "www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken",
             body = RequestBody.create(
                 json,
-                JsonObject(mapOf("token" to JsonPrimitive(customToken), "returnSecureToken" to JsonPrimitive(true))).toString()
+                JsonObject(mapOf("token" to JsonPrimitive(customToken), "returnSecureToken" to JsonPrimitive(true))).toString(),
             ),
             setResult = { responseBody ->
                 FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
-=======
-        val source =
-            enqueueAuthPost(
-                url = "www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken",
-                body =
-                    RequestBody.create(
-                        json,
-                        JsonObject(mapOf("token" to JsonPrimitive(customToken), "returnSecureToken" to JsonPrimitive(true))).toString(),
-                    ),
-                setResult = { responseBody ->
-                    FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
-                },
-            ).task.addOnSuccessListener {
-                updateByGetAccountInfo()
->>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
-            }
-        return source
+            },
+        ).task.continueWith {
+            updateByGetAccountInfo()
+        }
+        return source.result
     }
 
-    internal fun updateByGetAccountInfo(): Task<AuthResult> {
+    private fun updateByGetAccountInfo(): Task<AuthResult> {
         val source = TaskCompletionSource<AuthResult>()
 
         val body =
@@ -496,18 +436,6 @@ class FirebaseAuth constructor(
         return source.task
     }
 
-<<<<<<< HEAD
-    fun createUserWithEmailAndPassword(email: String, password: String): Task<AuthResult> {
-        val source = enqueueAuthPost(
-            url = "www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser",
-            body = RequestBody.create(
-                json,
-                JsonObject(
-                    mapOf(
-                        "email" to JsonPrimitive(email),
-                        "password" to JsonPrimitive(password),
-                        "returnSecureToken" to JsonPrimitive(true)
-=======
     fun createUserWithEmailAndPassword(
         email: String,
         password: String,
@@ -530,33 +458,12 @@ class FirebaseAuth constructor(
                     FirebaseUserImpl(
                         app = app,
                         data = jsonParser.parseToJsonElement(responseBody).jsonObject,
->>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
                     )
                 },
             )
         return source.task
     }
 
-<<<<<<< HEAD
-
-    fun signInWithEmailAndPassword(email: String, password: String): Task<AuthResult> {
-        val source = enqueueAuthPost(
-            url = "www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword",
-            body = RequestBody.create(
-                json,
-                JsonObject(
-                    mapOf(
-                        "email" to JsonPrimitive(email),
-                        "password" to JsonPrimitive(password),
-                        "returnSecureToken" to JsonPrimitive(true)
-                    )
-                ).toString()
-            ),
-            setResult = { responseBody ->
-                FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
-            }
-        )
-=======
     fun signInWithEmailAndPassword(
         email: String,
         password: String,
@@ -579,7 +486,6 @@ class FirebaseAuth constructor(
                     FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
                 },
             )
->>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
         return source.task
     }
 
@@ -600,11 +506,7 @@ class FirebaseAuth constructor(
                 ?: "UNKNOWN_ERROR",
             "$action API returned an error, " +
                 "with url [${request.method()}] ${request.url()} ${request.body()} -- " +
-<<<<<<< HEAD
-                "response [${response.code()}] ${response.message()} $body"
-=======
                 "response [${response.code()}] ${response.message()} $body",
->>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
         )
     }
 
@@ -913,20 +815,12 @@ class FirebaseAuth constructor(
         idTokenListeners.remove(listener)
     }
 
-<<<<<<< HEAD
     fun useEmulator(host: String, port: Int) {
         urlFactory = UrlFactory(app, "http://$host:$port/")
     }
 
     fun sendPasswordResetEmail(email: String, settings: ActionCodeSettings?): Task<Unit> = TODO()
     fun signInWithCredential(authCredential: AuthCredential): Task<AuthResult> = TODO()
-=======
-    fun sendPasswordResetEmail(
-        email: String,
-        settings: ActionCodeSettings?,
-    ): Task<Unit> = TODO()
-
->>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
     fun checkActionCode(code: String): Task<ActionCodeResult> = TODO()
 
     fun confirmPasswordReset(
@@ -957,14 +851,4 @@ class FirebaseAuth constructor(
     ): Task<AuthResult> = TODO()
 
     fun setLanguageCode(value: String): Nothing = TODO()
-<<<<<<< HEAD
-=======
-
-    fun useEmulator(
-        host: String,
-        port: Int,
-    ) {
-        urlFactory = UrlFactory(app, "http://$host:$port/")
-    }
->>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation)
 }
diff --git a/src/test/kotlin/AuthTest.kt b/src/test/kotlin/AuthTest.kt
index d603d5f..8326fd0 100644
--- a/src/test/kotlin/AuthTest.kt
+++ b/src/test/kotlin/AuthTest.kt
@@ -107,6 +107,6 @@ class AuthTest : FirebaseTest() {
                     }
                 }
 
-            assertEquals("INVALID_PASSWORD", exception.errorCode)
+            assertEquals("INVALID_LOGIN_CREDENTIALS", exception.errorCode)
         }
 }

From 744744267e153aba49769ebcf517d9c62a1e9fc4 Mon Sep 17 00:00:00 2001
From: jacob <jakub.kostka@augmy.org>
Date: Wed, 22 Jan 2025 10:17:13 +0100
Subject: [PATCH 23/23] Ktlint fixes

---
 src/main/java/android/net/Uri.kt              |   2 +-
 .../com/google/firebase/auth/FirebaseAuth.kt  | 202 +++++++++---------
 .../com/google/firebase/auth/FirebaseUser.kt  |   2 +-
 .../firebase/auth/UserProfileChangeRequest.kt |   4 +-
 src/test/kotlin/AppTest.kt                    |   4 +-
 src/test/kotlin/FirebaseAuthTest.kt           |   2 +-
 src/test/kotlin/FirebaseTest.kt               |   5 +-
 7 files changed, 110 insertions(+), 111 deletions(-)

diff --git a/src/main/java/android/net/Uri.kt b/src/main/java/android/net/Uri.kt
index c14bc76..e88c0f2 100644
--- a/src/main/java/android/net/Uri.kt
+++ b/src/main/java/android/net/Uri.kt
@@ -4,7 +4,7 @@ import java.net.URI
 import java.util.Collections
 
 class Uri(
-    private val uri: URI,
+    private val uri: URI
 ) {
     companion object {
         @JvmStatic
diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
index 5198027..1f9c883 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.kt
@@ -46,7 +46,7 @@ internal val jsonParser = Json { ignoreUnknownKeys = true }
 
 class UrlFactory(
     private val app: FirebaseApp,
-    private val emulatorUrl: String? = null,
+    private val emulatorUrl: String? = null
 ) {
     fun buildUrl(uri: String): String = "${emulatorUrl ?: "https://"}$uri?key=${app.options.apiKey}"
 }
@@ -65,7 +65,7 @@ class FirebaseUserImpl internal constructor(
     override val photoUrl: String?,
     override val displayName: String?,
     @Transient
-    private val urlFactory: UrlFactory = UrlFactory(app),
+    private val urlFactory: UrlFactory = UrlFactory(app)
 ) : FirebaseUser() {
     constructor(
         app: FirebaseApp,
@@ -74,14 +74,14 @@ class FirebaseUserImpl internal constructor(
         email: String? = data.getOrElse("email") { null }?.jsonPrimitive?.contentOrNull,
         photoUrl: String? = data.getOrElse("photoUrl") { null }?.jsonPrimitive?.contentOrNull,
         displayName: String? = data.getOrElse("displayName") { null }?.jsonPrimitive?.contentOrNull,
-        urlFactory: UrlFactory = UrlFactory(app),
+        urlFactory: UrlFactory = UrlFactory(app)
     ) : this(
         app = app,
         isAnonymous = isAnonymous,
         uid =
-            data["uid"]?.jsonPrimitive?.contentOrNull ?: data["user_id"]?.jsonPrimitive?.contentOrNull
-                ?: data["localId"]?.jsonPrimitive?.contentOrNull
-                ?: "",
+        data["uid"]?.jsonPrimitive?.contentOrNull ?: data["user_id"]?.jsonPrimitive?.contentOrNull
+            ?: data["localId"]?.jsonPrimitive?.contentOrNull
+            ?: "",
         idToken = data["idToken"]?.jsonPrimitive?.contentOrNull ?: data.getValue("id_token").jsonPrimitive.content,
         refreshToken = data["refreshToken"]?.jsonPrimitive?.contentOrNull ?: data.getValue("refresh_token").jsonPrimitive.content,
         expiresIn = data["expiresIn"]?.jsonPrimitive?.intOrNull ?: data.getValue("expires_in").jsonPrimitive.int,
@@ -89,7 +89,7 @@ class FirebaseUserImpl internal constructor(
         email = email,
         photoUrl = photoUrl ?: data["photo_url"]?.jsonPrimitive?.contentOrNull,
         displayName = displayName ?: data["display_name"]?.jsonPrimitive?.contentOrNull,
-        urlFactory = urlFactory,
+        urlFactory = urlFactory
     )
 
     val claims: Map<String, Any?> by lazy {
@@ -122,7 +122,7 @@ class FirebaseUserImpl internal constructor(
             object : Callback {
                 override fun onFailure(
                     call: Call,
-                    e: IOException,
+                    e: IOException
                 ) {
                     source.setException(FirebaseException(e.toString(), e))
                 }
@@ -130,7 +130,7 @@ class FirebaseUserImpl internal constructor(
                 @Throws(IOException::class)
                 override fun onResponse(
                     call: Call,
-                    response: Response,
+                    response: Response
                 ) {
                     if (!response.isSuccessful) {
                         FirebaseAuth.getInstance(app).signOut()
@@ -138,14 +138,14 @@ class FirebaseUserImpl internal constructor(
                             FirebaseAuth.getInstance(app).createAuthInvalidUserException(
                                 "deleteAccount",
                                 request,
-                                response,
-                            ),
+                                response
+                            )
                         )
                     } else {
                         source.setResult(null)
                     }
                 }
-            },
+            }
         )
         return source.task
     }
@@ -161,7 +161,7 @@ class FirebaseUserImpl internal constructor(
     // TODO implement ActionCodeSettings and pass it to the url
     override fun verifyBeforeUpdateEmail(
         newEmail: String,
-        actionCodeSettings: ActionCodeSettings?,
+        actionCodeSettings: ActionCodeSettings?
     ): Task<Unit> {
         val source = TaskCompletionSource<Unit>()
         val body =
@@ -172,9 +172,9 @@ class FirebaseUserImpl internal constructor(
                         "idToken" to JsonPrimitive(idToken),
                         "email" to JsonPrimitive(email),
                         "newEmail" to JsonPrimitive(newEmail),
-                        "requestType" to JsonPrimitive(OobRequestType.VERIFY_AND_CHANGE_EMAIL.name),
-                    ),
-                ).toString(),
+                        "requestType" to JsonPrimitive(OobRequestType.VERIFY_AND_CHANGE_EMAIL.name)
+                    )
+                ).toString()
             )
         val request =
             Request
@@ -186,7 +186,7 @@ class FirebaseUserImpl internal constructor(
             object : Callback {
                 override fun onFailure(
                     call: Call,
-                    e: IOException,
+                    e: IOException
                 ) {
                     source.setException(FirebaseException(e.toString(), e))
                     e.printStackTrace()
@@ -195,7 +195,7 @@ class FirebaseUserImpl internal constructor(
                 @Throws(IOException::class)
                 override fun onResponse(
                     call: Call,
-                    response: Response,
+                    response: Response
                 ) {
                     if (!response.isSuccessful) {
                         FirebaseAuth.getInstance(app).signOut()
@@ -203,14 +203,14 @@ class FirebaseUserImpl internal constructor(
                             FirebaseAuth.getInstance(app).createAuthInvalidUserException(
                                 "verifyEmail",
                                 request,
-                                response,
-                            ),
+                                response
+                            )
                         )
                     } else {
                         source.setResult(null)
                     }
                 }
-            },
+            }
         )
         return source.task
     }
@@ -221,7 +221,7 @@ class FirebaseUserImpl internal constructor(
 
     fun updateProfile(
         displayName: String?,
-        photoUrl: String?,
+        photoUrl: String?
     ): Task<Unit> {
         val request =
             UserProfileChangeRequest
@@ -234,7 +234,7 @@ class FirebaseUserImpl internal constructor(
 }
 
 class FirebaseAuth constructor(
-    val app: FirebaseApp,
+    val app: FirebaseApp
 ) : InternalAuthProvider {
     internal val json = MediaType.parse("application/json; charset=utf-8")
     internal val client: OkHttpClient =
@@ -248,7 +248,7 @@ class FirebaseAuth constructor(
     private fun enqueueAuthPost(
         url: String,
         body: RequestBody,
-        setResult: (responseBody: String) -> FirebaseUserImpl?,
+        setResult: (responseBody: String) -> FirebaseUserImpl?
     ): TaskCompletionSource<AuthResult> {
         val source = TaskCompletionSource<AuthResult>()
         val request = Request.Builder()
@@ -260,7 +260,7 @@ class FirebaseAuth constructor(
             object : Callback {
                 override fun onFailure(
                     call: Call,
-                    e: IOException,
+                    e: IOException
                 ) {
                     source.setException(FirebaseException(e.toString(), e))
                 }
@@ -268,25 +268,25 @@ class FirebaseAuth constructor(
                 @Throws(IOException::class)
                 override fun onResponse(
                     call: Call,
-                    response: Response,
+                    response: Response
                 ) {
                     if (!response.isSuccessful) {
                         source.setException(
-                            createAuthInvalidUserException("accounts", request, response),
+                            createAuthInvalidUserException("accounts", request, response)
                         )
                     } else {
                         if (response.body()?.use { it.string() }?.also { responseBody ->
-                                user = setResult(responseBody)
-                                source.setResult(AuthResult { user })
-                            } == null
+                            user = setResult(responseBody)
+                            source.setResult(AuthResult { user })
+                        } == null
                         ) {
                             source.setException(
-                                createAuthInvalidUserException("accounts", request, response),
+                                createAuthInvalidUserException("accounts", request, response)
                             )
                         }
                     }
                 }
-            },
+            }
         )
         return source
     }
@@ -351,7 +351,7 @@ class FirebaseAuth constructor(
             body = RequestBody.create(json, JsonObject(mapOf("returnSecureToken" to JsonPrimitive(true))).toString()),
             setResult = { responseBody ->
                 FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject, isAnonymous = true)
-            },
+            }
         )
         return source.task
     }
@@ -361,11 +361,11 @@ class FirebaseAuth constructor(
             url = "www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken",
             body = RequestBody.create(
                 json,
-                JsonObject(mapOf("token" to JsonPrimitive(customToken), "returnSecureToken" to JsonPrimitive(true))).toString(),
+                JsonObject(mapOf("token" to JsonPrimitive(customToken), "returnSecureToken" to JsonPrimitive(true))).toString()
             ),
             setResult = { responseBody ->
                 FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
-            },
+            }
         ).task.continueWith {
             updateByGetAccountInfo()
         }
@@ -378,7 +378,7 @@ class FirebaseAuth constructor(
         val body =
             RequestBody.create(
                 json,
-                JsonObject(mapOf("idToken" to JsonPrimitive(user?.idToken))).toString(),
+                JsonObject(mapOf("idToken" to JsonPrimitive(user?.idToken))).toString()
             )
         val request =
             Request
@@ -391,7 +391,7 @@ class FirebaseAuth constructor(
             object : Callback {
                 override fun onFailure(
                     call: Call,
-                    e: IOException,
+                    e: IOException
                 ) {
                     source.setException(FirebaseException(e.toString(), e))
                 }
@@ -399,17 +399,17 @@ class FirebaseAuth constructor(
                 @Throws(IOException::class)
                 override fun onResponse(
                     call: Call,
-                    response: Response,
+                    response: Response
                 ) {
                     if (!response.isSuccessful) {
                         source.setException(
-                            createAuthInvalidUserException("updateWithAccountInfo", request, response),
+                            createAuthInvalidUserException("updateWithAccountInfo", request, response)
                         )
                     } else {
                         val newBody =
                             jsonParser
                                 .parseToJsonElement(
-                                    response.body()?.use { it.string() } ?: "",
+                                    response.body()?.use { it.string() } ?: ""
                                 ).jsonObject
 
                         user?.let { prev ->
@@ -424,67 +424,67 @@ class FirebaseAuth constructor(
                                     createdAt = newBody["createdAt"]?.jsonPrimitive?.longOrNull ?: prev.createdAt,
                                     email = newBody["email"]?.jsonPrimitive?.contentOrNull ?: prev.email,
                                     photoUrl = newBody["photoUrl"]?.jsonPrimitive?.contentOrNull ?: prev.photoUrl,
-                                    displayName = newBody["displayName"]?.jsonPrimitive?.contentOrNull ?: prev.displayName,
+                                    displayName = newBody["displayName"]?.jsonPrimitive?.contentOrNull ?: prev.displayName
                                 )
                             source.setResult(AuthResult { user })
                         }
                         source.setResult(null)
                     }
                 }
-            },
+            }
         )
         return source.task
     }
 
     fun createUserWithEmailAndPassword(
         email: String,
-        password: String,
+        password: String
     ): Task<AuthResult> {
         val source =
             enqueueAuthPost(
                 url = "www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser",
                 body =
-                    RequestBody.create(
-                        json,
-                        JsonObject(
-                            mapOf(
-                                "email" to JsonPrimitive(email),
-                                "password" to JsonPrimitive(password),
-                                "returnSecureToken" to JsonPrimitive(true),
-                            ),
-                        ).toString(),
-                    ),
+                RequestBody.create(
+                    json,
+                    JsonObject(
+                        mapOf(
+                            "email" to JsonPrimitive(email),
+                            "password" to JsonPrimitive(password),
+                            "returnSecureToken" to JsonPrimitive(true)
+                        )
+                    ).toString()
+                ),
                 setResult = { responseBody ->
                     FirebaseUserImpl(
                         app = app,
-                        data = jsonParser.parseToJsonElement(responseBody).jsonObject,
+                        data = jsonParser.parseToJsonElement(responseBody).jsonObject
                     )
-                },
+                }
             )
         return source.task
     }
 
     fun signInWithEmailAndPassword(
         email: String,
-        password: String,
+        password: String
     ): Task<AuthResult> {
         val source =
             enqueueAuthPost(
                 url = "www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword",
                 body =
-                    RequestBody.create(
-                        json,
-                        JsonObject(
-                            mapOf(
-                                "email" to JsonPrimitive(email),
-                                "password" to JsonPrimitive(password),
-                                "returnSecureToken" to JsonPrimitive(true),
-                            ),
-                        ).toString(),
-                    ),
+                RequestBody.create(
+                    json,
+                    JsonObject(
+                        mapOf(
+                            "email" to JsonPrimitive(email),
+                            "password" to JsonPrimitive(password),
+                            "returnSecureToken" to JsonPrimitive(true)
+                        )
+                    ).toString()
+                ),
                 setResult = { responseBody ->
                     FirebaseUserImpl(app, jsonParser.parseToJsonElement(responseBody).jsonObject)
-                },
+                }
             )
         return source.task
     }
@@ -492,7 +492,7 @@ class FirebaseAuth constructor(
     internal fun createAuthInvalidUserException(
         action: String,
         request: Request,
-        response: Response,
+        response: Response
     ): FirebaseAuthInvalidUserException {
         val body = response.body()!!.use { it.string() }
         val jsonObject = jsonParser.parseToJsonElement(body).jsonObject
@@ -506,7 +506,7 @@ class FirebaseAuth constructor(
                 ?: "UNKNOWN_ERROR",
             "$action API returned an error, " +
                 "with url [${request.method()}] ${request.url()} ${request.body()} -- " +
-                "response [${response.code()}] ${response.message()} $body",
+                "response [${response.code()}] ${response.message()} $body"
         )
     }
 
@@ -544,7 +544,7 @@ class FirebaseAuth constructor(
     internal fun <T> refreshToken(
         user: FirebaseUserImpl,
         source: TaskCompletionSource<T>,
-        map: (user: FirebaseUserImpl) -> T?,
+        map: (user: FirebaseUserImpl) -> T?
     ) {
         refreshSource = refreshSource
             .takeUnless { it.task.isComplete }
@@ -561,9 +561,9 @@ class FirebaseAuth constructor(
                 JsonObject(
                     mapOf(
                         "refresh_token" to JsonPrimitive(user.refreshToken),
-                        "grant_type" to JsonPrimitive("refresh_token"),
-                    ),
-                ).toString(),
+                        "grant_type" to JsonPrimitive("refresh_token")
+                    )
+                ).toString()
             )
         val request =
             Request
@@ -577,7 +577,7 @@ class FirebaseAuth constructor(
             object : Callback {
                 override fun onFailure(
                     call: Call,
-                    e: IOException,
+                    e: IOException
                 ) {
                     source.setException(e)
                 }
@@ -585,7 +585,7 @@ class FirebaseAuth constructor(
                 @Throws(IOException::class)
                 override fun onResponse(
                     call: Call,
-                    response: Response,
+                    response: Response
                 ) {
                     val responseBody = response.body()?.use { it.string() }
 
@@ -597,7 +597,7 @@ class FirebaseAuth constructor(
                             if (newUser.claims["aud"] != app.options.projectId) {
                                 signOutAndThrowInvalidUserException(
                                     newUser.claims.toString(),
-                                    "Project ID's do not match ${newUser.claims["aud"]} != ${app.options.projectId}",
+                                    "Project ID's do not match ${newUser.claims["aud"]} != ${app.options.projectId}"
                                 )
                             } else {
                                 this@FirebaseAuth.user = newUser
@@ -609,12 +609,12 @@ class FirebaseAuth constructor(
 
                 private fun signOutAndThrowInvalidUserException(
                     body: String,
-                    message: String,
+                    message: String
                 ) {
                     signOut()
                     source.setException(FirebaseAuthInvalidUserException(body, message))
                 }
-            },
+            }
         )
         return source
     }
@@ -629,9 +629,9 @@ class FirebaseAuth constructor(
                     mapOf(
                         "idToken" to JsonPrimitive(user?.idToken),
                         "email" to JsonPrimitive(email),
-                        "returnSecureToken" to JsonPrimitive(true),
-                    ),
-                ).toString(),
+                        "returnSecureToken" to JsonPrimitive(true)
+                    )
+                ).toString()
             )
         val request =
             Request
@@ -644,7 +644,7 @@ class FirebaseAuth constructor(
             object : Callback {
                 override fun onFailure(
                     call: Call,
-                    e: IOException,
+                    e: IOException
                 ) {
                     source.setException(FirebaseException(e.toString(), e))
                 }
@@ -652,7 +652,7 @@ class FirebaseAuth constructor(
                 @Throws(IOException::class)
                 override fun onResponse(
                     call: Call,
-                    response: Response,
+                    response: Response
                 ) {
                     if (!response.isSuccessful) {
                         signOut()
@@ -660,14 +660,14 @@ class FirebaseAuth constructor(
                             createAuthInvalidUserException(
                                 "updateEmail",
                                 request,
-                                response,
-                            ),
+                                response
+                            )
                         )
                     } else {
                         val newBody =
                             jsonParser
                                 .parseToJsonElement(
-                                    response.body()?.use { it.string() } ?: "",
+                                    response.body()?.use { it.string() } ?: ""
                                 ).jsonObject
 
                         user?.let { prev ->
@@ -682,13 +682,13 @@ class FirebaseAuth constructor(
                                     createdAt = prev.createdAt,
                                     email = newBody["newEmail"]?.jsonPrimitive?.contentOrNull ?: prev.email,
                                     photoUrl = newBody["photoUrl"]?.jsonPrimitive?.contentOrNull ?: prev.photoUrl,
-                                    displayName = newBody["displayName"]?.jsonPrimitive?.contentOrNull ?: prev.displayName,
+                                    displayName = newBody["displayName"]?.jsonPrimitive?.contentOrNull ?: prev.displayName
                                 )
                         }
                         source.setResult(null)
                     }
                 }
-            },
+            }
         )
         return source.task
     }
@@ -704,9 +704,9 @@ class FirebaseAuth constructor(
                         "idToken" to JsonPrimitive(user?.idToken),
                         "displayName" to JsonPrimitive(request.displayName),
                         "photoUrl" to JsonPrimitive(request.photoUrl),
-                        "returnSecureToken" to JsonPrimitive(true),
-                    ),
-                ).toString(),
+                        "returnSecureToken" to JsonPrimitive(true)
+                    )
+                ).toString()
             )
         val req =
             Request
@@ -719,7 +719,7 @@ class FirebaseAuth constructor(
             object : Callback {
                 override fun onFailure(
                     call: Call,
-                    e: IOException,
+                    e: IOException
                 ) {
                     source.setException(FirebaseException(e.toString(), e))
                 }
@@ -727,7 +727,7 @@ class FirebaseAuth constructor(
                 @Throws(IOException::class)
                 override fun onResponse(
                     call: Call,
-                    response: Response,
+                    response: Response
                 ) {
                     if (!response.isSuccessful) {
                         signOut()
@@ -735,14 +735,14 @@ class FirebaseAuth constructor(
                             createAuthInvalidUserException(
                                 "updateProfile",
                                 req,
-                                response,
-                            ),
+                                response
+                            )
                         )
                     } else {
                         val newBody =
                             jsonParser
                                 .parseToJsonElement(
-                                    response.body()?.use { it.string() } ?: "",
+                                    response.body()?.use { it.string() } ?: ""
                                 ).jsonObject
 
                         user?.let { prev ->
@@ -757,13 +757,13 @@ class FirebaseAuth constructor(
                                     createdAt = prev.createdAt,
                                     email = newBody["newEmail"]?.jsonPrimitive?.contentOrNull ?: prev.email,
                                     photoUrl = newBody["photoUrl"]?.jsonPrimitive?.contentOrNull ?: prev.photoUrl,
-                                    displayName = newBody["displayName"]?.jsonPrimitive?.contentOrNull ?: prev.displayName,
+                                    displayName = newBody["displayName"]?.jsonPrimitive?.contentOrNull ?: prev.displayName
                                 )
                         }
                         source.setResult(null)
                     }
                 }
-            },
+            }
         )
         return source.task
     }
@@ -825,14 +825,14 @@ class FirebaseAuth constructor(
 
     fun confirmPasswordReset(
         code: String,
-        newPassword: String,
+        newPassword: String
     ): Task<Unit> = TODO()
 
     fun fetchSignInMethodsForEmail(email: String): Task<SignInMethodQueryResult> = TODO()
 
     fun sendSignInLinkToEmail(
         email: String,
-        actionCodeSettings: ActionCodeSettings,
+        actionCodeSettings: ActionCodeSettings
     ): Task<Unit> = TODO()
 
     fun verifyPasswordResetCode(code: String): Task<String> = TODO()
@@ -847,7 +847,7 @@ class FirebaseAuth constructor(
 
     fun signInWithEmailLink(
         email: String,
-        link: String,
+        link: String
     ): Task<AuthResult> = TODO()
 
     fun setLanguageCode(value: String): Nothing = TODO()
diff --git a/src/main/java/com/google/firebase/auth/FirebaseUser.kt b/src/main/java/com/google/firebase/auth/FirebaseUser.kt
index 0932518..63b3e93 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseUser.kt
+++ b/src/main/java/com/google/firebase/auth/FirebaseUser.kt
@@ -15,7 +15,7 @@ abstract class FirebaseUser {
 
     abstract fun verifyBeforeUpdateEmail(
         newEmail: String,
-        actionCodeSettings: ActionCodeSettings?,
+        actionCodeSettings: ActionCodeSettings?
     ): Task<Unit>
 
     abstract fun updateEmail(email: String): Task<Unit>
diff --git a/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.kt b/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.kt
index c60bb83..885e53a 100644
--- a/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.kt
+++ b/src/main/java/com/google/firebase/auth/UserProfileChangeRequest.kt
@@ -6,13 +6,13 @@ import android.os.Parcelable
 
 class UserProfileChangeRequest private constructor(
     internal val displayName: String?,
-    internal val photoUrl: String?,
+    internal val photoUrl: String?
 ) : Parcelable {
     override fun describeContents(): Int = displayName.hashCode() + photoUrl.hashCode()
 
     override fun writeToParcel(
         dest: Parcel,
-        flags: Int,
+        flags: Int
     ) {
         dest.writeString(displayName)
         dest.writeString(photoUrl)
diff --git a/src/test/kotlin/AppTest.kt b/src/test/kotlin/AppTest.kt
index 2e5697f..c7e5b65 100644
--- a/src/test/kotlin/AppTest.kt
+++ b/src/test/kotlin/AppTest.kt
@@ -14,7 +14,7 @@ class AppTest : FirebaseTest() {
 
                 override fun store(
                     key: String,
-                    value: String,
+                    value: String
                 ) = storage.set(key, value)
 
                 override fun retrieve(key: String) = storage[key]
@@ -24,7 +24,7 @@ class AppTest : FirebaseTest() {
                 }
 
                 override fun log(msg: String) = println(msg)
-            },
+            }
         )
         val options =
             FirebaseOptions
diff --git a/src/test/kotlin/FirebaseAuthTest.kt b/src/test/kotlin/FirebaseAuthTest.kt
index b2000f2..cc18aeb 100644
--- a/src/test/kotlin/FirebaseAuthTest.kt
+++ b/src/test/kotlin/FirebaseAuthTest.kt
@@ -35,7 +35,7 @@ internal class FirebaseAuthTest : FirebaseTest() {
                         .Builder()
                         .setDisplayName("testDisplayName")
                         .setPhotoUri(Uri.parse("https://picsum.photos/100"))
-                        .build(),
+                        .build()
                 )?.await()
             assertEquals("testDisplayName", auth.currentUser?.displayName)
             assertEquals("https://picsum.photos/100", auth.currentUser?.photoUrl)
diff --git a/src/test/kotlin/FirebaseTest.kt b/src/test/kotlin/FirebaseTest.kt
index a714f8c..bc28896 100644
--- a/src/test/kotlin/FirebaseTest.kt
+++ b/src/test/kotlin/FirebaseTest.kt
@@ -5,7 +5,6 @@ import com.google.firebase.FirebaseOptions
 import com.google.firebase.FirebasePlatform
 import com.google.firebase.auth.FirebaseAuth
 import com.google.firebase.initialize
-import com.google.firebase.ktx.initialize
 import org.junit.After
 import org.junit.Before
 import java.io.File
@@ -36,7 +35,7 @@ abstract class FirebaseTest {
 
                 override fun store(
                     key: String,
-                    value: String,
+                    value: String
                 ) = storage.set(key, value)
 
                 override fun retrieve(key: String) = storage[key]
@@ -48,7 +47,7 @@ abstract class FirebaseTest {
                 override fun log(msg: String) = println(msg)
 
                 override fun getDatabasePath(name: String) = File("./build/$name")
-            },
+            }
         )
         val options =
             FirebaseOptions