From f63e1b392a612cbcd1c04b4434e2644e674c744f Mon Sep 17 00:00:00 2001 From: Jakub Kostka 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 { + val source = TaskCompletionSource() + 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 { val source = TaskCompletionSource() val body = RequestBody.create( @@ -425,7 +457,6 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider { } fun sendPasswordResetEmail(email: String, settings: ActionCodeSettings?): Task = TODO() - fun createUserWithEmailAndPassword(email: String, password: String): Task = TODO() fun signInWithCredential(authCredential: AuthCredential): Task = TODO() fun checkActionCode(code: String): Task = TODO() fun confirmPasswordReset(code: String, newPassword: String): Task = TODO() From 0972225904fc1d5873a77f0303e49ac05f037204 Mon Sep 17 00:00:00 2001 From: Jakub Kostka 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 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 abstract fun reload(): Task + 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() + 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 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 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 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 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 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 = FirebaseAuth.getInstance(app).updateEmail(email) + override fun reload(): Task { val source = TaskCompletionSource() FirebaseAuth.getInstance(app).refreshToken(this, source) { null } return source.task } + override fun verifyBeforeUpdateEmail( + newEmail: String, + actionCodeSettings: ActionCodeSettings? + ): Task { + val source = TaskCompletionSource() + 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 { + val source = TaskCompletionSource() + 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() @@ -192,127 +279,67 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider { } fun signInAnonymously(): Task { - val source = TaskCompletionSource() - 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 { - val source = TaskCompletionSource() - 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 { - val source = TaskCompletionSource() - 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 { - val source = TaskCompletionSource() - 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 { + val source = TaskCompletionSource() + + 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 = TODO() - fun signInWithCredential(authCredential: AuthCredential): Task = TODO() fun checkActionCode(code: String): Task = TODO() fun confirmPasswordReset(code: String, newPassword: String): Task = TODO() fun fetchSignInMethodsForEmail(email: String): Task = 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 abstract fun reload(): Task - abstract val email: String + abstract fun verifyBeforeUpdateEmail(newEmail: String, actionCodeSettings: ActionCodeSettings?): Task + abstract fun updateEmail(email: String): Task val displayName: String get() = TODO() val phoneNumber: String get() = TODO() @@ -22,11 +24,9 @@ abstract class FirebaseUser { fun sendEmailVerification(): Task = TODO() fun sendEmailVerification(actionCodeSettings: ActionCodeSettings): Task = TODO() fun unlink(provider: String): Task = TODO() - fun updateEmail(email: String): Task = TODO() fun updatePassword(password: String): Task = TODO() fun updatePhoneNumber(credential: AuthCredential): Task = TODO() fun updateProfile(request: UserProfileChangeRequest): Task = TODO() - fun verifyBeforeUpdateEmail(newEmail: String, actionCodeSettings: ActionCodeSettings?): Task = TODO() fun reauthenticate(credential: AuthCredential): Task = TODO() fun reauthenticateAndRetrieveData(credential: AuthCredential): Task = 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 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 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 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 { val source = TaskCompletionSource() 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 { val source = TaskCompletionSource() - 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 = FirebaseAuth.getInstance(app).updateProfile(request) + + fun updateProfile( + displayName: String?, + photoUrl: String?, + ): Task { + 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 { val source = TaskCompletionSource() - 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 { - 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 { - 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 { - val source = enqueueAuthPost( - url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser", - body = RequestBody.create( + internal fun updateByGetAccountInfo(): Task { + val source = TaskCompletionSource() + + 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 { - 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 { + 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 { + 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().apply { setException(Exception()) } - internal fun refreshToken(user: FirebaseUserImpl, source: TaskCompletionSource, map: (user: FirebaseUserImpl) -> T?) { + internal fun refreshToken( + user: FirebaseUserImpl, + source: TaskCompletionSource, + 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 { val source = TaskCompletionSource() - 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 { val source = TaskCompletionSource() - 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 { + val source = TaskCompletionSource() - 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 = TODO() + fun sendPasswordResetEmail( + email: String, + settings: ActionCodeSettings?, + ): Task = TODO() + fun checkActionCode(code: String): Task = TODO() - fun confirmPasswordReset(code: String, newPassword: String): Task = TODO() + + fun confirmPasswordReset( + code: String, + newPassword: String, + ): Task = TODO() + fun fetchSignInMethodsForEmail(email: String): Task = TODO() - fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings): Task = TODO() + + fun sendSignInLinkToEmail( + email: String, + actionCodeSettings: ActionCodeSettings, + ): Task = TODO() + fun verifyPasswordResetCode(code: String): Task = TODO() + fun updateCurrentUser(user: FirebaseUser): Task = TODO() + fun applyActionCode(code: String): Task = TODO() + val languageCode: String get() = TODO() + fun isSignInWithEmailLink(link: String): Boolean = TODO() - fun signInWithEmailLink(email: String, link: String): Task = TODO() + + fun signInWithEmailLink( + email: String, + link: String, + ): Task = 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 + abstract fun reload(): Task - abstract fun verifyBeforeUpdateEmail(newEmail: String, actionCodeSettings: ActionCodeSettings?): Task + + abstract fun verifyBeforeUpdateEmail( + newEmail: String, + actionCodeSettings: ActionCodeSettings?, + ): Task + abstract fun updateEmail(email: String): Task - val displayName: String get() = TODO() + abstract fun getIdToken(forceRefresh: Boolean): Task + + abstract fun updateProfile(request: UserProfileChangeRequest): Task + 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 get() = TODO() val providerId: String get() = TODO() - abstract fun getIdToken(forceRefresh: Boolean): Task + fun linkWithCredential(credential: AuthCredential): Task = TODO() + fun sendEmailVerification(): Task = TODO() + fun sendEmailVerification(actionCodeSettings: ActionCodeSettings): Task = TODO() + fun unlink(provider: String): Task = TODO() + fun updatePassword(password: String): Task = TODO() + fun updatePhoneNumber(credential: AuthCredential): Task = TODO() - fun updateProfile(request: UserProfileChangeRequest): Task = TODO() + fun reauthenticate(credential: AuthCredential): Task = TODO() + fun reauthenticateAndRetrieveData(credential: AuthCredential): Task = 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 { + override fun createFromParcel(parcel: Parcel): UserProfileChangeRequest { + val displayName = parcel.readString() + val photoUri = parcel.readString() + return UserProfileChangeRequest(displayName, photoUri) + } + + override fun newArray(size: Int): Array = 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() - 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() + + 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() - 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() - 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() + + 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 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 { 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 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 @@ -

Firebase Java SDK GitHub last commit

+

Firebase Java SDK GitHub last commit

Built and maintained with 🧡 by GitLive
Development teams merge faster with GitLive
From bce0cd2d3d88559c593ce58a9a538f2914b15ea9 Mon Sep 17 00:00:00 2001 From: Jakub Kostka 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 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 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 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 = FirebaseAuth.getInstance(app).updateEmail(email) + override fun reload(): Task { val source = TaskCompletionSource() FirebaseAuth.getInstance(app).refreshToken(this, source) { null } return source.task } + override fun verifyBeforeUpdateEmail( + newEmail: String, + actionCodeSettings: ActionCodeSettings? + ): Task { + val source = TaskCompletionSource() + 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 { + val source = TaskCompletionSource() + 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() @@ -192,127 +279,67 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider { } fun signInAnonymously(): Task { - val source = TaskCompletionSource() - 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 { - val source = TaskCompletionSource() - 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 { - val source = TaskCompletionSource() - 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 { - val source = TaskCompletionSource() - 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 { + val source = TaskCompletionSource() + + 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 = TODO() - fun signInWithCredential(authCredential: AuthCredential): Task = TODO() fun checkActionCode(code: String): Task = TODO() fun confirmPasswordReset(code: String, newPassword: String): Task = TODO() fun fetchSignInMethodsForEmail(email: String): Task = 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 abstract fun reload(): Task - abstract val email: String + abstract fun verifyBeforeUpdateEmail(newEmail: String, actionCodeSettings: ActionCodeSettings?): Task + abstract fun updateEmail(email: String): Task val displayName: String get() = TODO() val phoneNumber: String get() = TODO() @@ -22,11 +24,9 @@ abstract class FirebaseUser { fun sendEmailVerification(): Task = TODO() fun sendEmailVerification(actionCodeSettings: ActionCodeSettings): Task = TODO() fun unlink(provider: String): Task = TODO() - fun updateEmail(email: String): Task = TODO() fun updatePassword(password: String): Task = TODO() fun updatePhoneNumber(credential: AuthCredential): Task = TODO() fun updateProfile(request: UserProfileChangeRequest): Task = TODO() - fun verifyBeforeUpdateEmail(newEmail: String, actionCodeSettings: ActionCodeSettings?): Task = TODO() fun reauthenticate(credential: AuthCredential): Task = TODO() fun reauthenticateAndRetrieveData(credential: AuthCredential): Task = 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 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?= 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 by lazy { @@ -92,7 +105,7 @@ class FirebaseUserImpl internal constructor( val source = TaskCompletionSource() 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 { val source = TaskCompletionSource() 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 { 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 { 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 { 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 { 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 = TODO() + fun signInWithCredential(authCredential: AuthCredential): Task = TODO() fun checkActionCode(code: String): Task = TODO() fun confirmPasswordReset(code: String, newPassword: String): Task = TODO() fun fetchSignInMethodsForEmail(email: String): Task = TODO() @@ -568,5 +589,4 @@ class FirebaseAuth constructor(val app: FirebaseApp) : InternalAuthProvider { fun signInWithEmailLink(email: String, link: String): Task = 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() + 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() - 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 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 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 { val source = TaskCompletionSource() 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 { val source = TaskCompletionSource() - 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 = FirebaseAuth.getInstance(app).updateProfile(request) + + fun updateProfile( + displayName: String?, + photoUrl: String?, + ): Task { + 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 { val source = TaskCompletionSource() +<<<<<<< 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 { +<<<<<<< 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 { +<<<<<<< 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 { + val source = TaskCompletionSource() + + 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 { 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 { + 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 { 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 { + 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().apply { setException(Exception()) } - internal fun refreshToken(user: FirebaseUserImpl, source: TaskCompletionSource, map: (user: FirebaseUserImpl) -> T?) { + internal fun refreshToken( + user: FirebaseUserImpl, + source: TaskCompletionSource, + 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 { val source = TaskCompletionSource() - 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 { val source = TaskCompletionSource() - 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 { + val source = TaskCompletionSource() - 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 = TODO() fun signInWithCredential(authCredential: AuthCredential): Task = TODO() +======= + fun sendPasswordResetEmail( + email: String, + settings: ActionCodeSettings?, + ): Task = TODO() + +>>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation) fun checkActionCode(code: String): Task = TODO() - fun confirmPasswordReset(code: String, newPassword: String): Task = TODO() + + fun confirmPasswordReset( + code: String, + newPassword: String, + ): Task = TODO() + fun fetchSignInMethodsForEmail(email: String): Task = TODO() - fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings): Task = TODO() + + fun sendSignInLinkToEmail( + email: String, + actionCodeSettings: ActionCodeSettings, + ): Task = TODO() + fun verifyPasswordResetCode(code: String): Task = TODO() + fun updateCurrentUser(user: FirebaseUser): Task = TODO() + fun applyActionCode(code: String): Task = TODO() + val languageCode: String get() = TODO() + fun isSignInWithEmailLink(link: String): Boolean = TODO() - fun signInWithEmailLink(email: String, link: String): Task = TODO() + + fun signInWithEmailLink( + email: String, + link: String, + ): Task = 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 + abstract fun reload(): Task - abstract fun verifyBeforeUpdateEmail(newEmail: String, actionCodeSettings: ActionCodeSettings?): Task + + abstract fun verifyBeforeUpdateEmail( + newEmail: String, + actionCodeSettings: ActionCodeSettings?, + ): Task + abstract fun updateEmail(email: String): Task - val displayName: String get() = TODO() + abstract fun getIdToken(forceRefresh: Boolean): Task + + abstract fun updateProfile(request: UserProfileChangeRequest): Task + 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 get() = TODO() val providerId: String get() = TODO() - abstract fun getIdToken(forceRefresh: Boolean): Task + fun linkWithCredential(credential: AuthCredential): Task = TODO() + fun sendEmailVerification(): Task = TODO() + fun sendEmailVerification(actionCodeSettings: ActionCodeSettings): Task = TODO() + fun unlink(provider: String): Task = TODO() + fun updatePassword(password: String): Task = TODO() + fun updatePhoneNumber(credential: AuthCredential): Task = TODO() - fun updateProfile(request: UserProfileChangeRequest): Task = TODO() + fun reauthenticate(credential: AuthCredential): Task = TODO() + fun reauthenticateAndRetrieveData(credential: AuthCredential): Task = 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 { + override fun createFromParcel(parcel: Parcel): UserProfileChangeRequest { + val displayName = parcel.readString() + val photoUri = parcel.readString() + return UserProfileChangeRequest(displayName, photoUri) + } + + override fun newArray(size: Int): Array = 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() - 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() + + 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() - 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() - 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() + + 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 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 { 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 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 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: dev.gitlive firebase-java-sdk - 0.4.6 + 0.4.7 ``` From 6ee896e13aac6ce907cc2b3e5121a45bb5f0f945 Mon Sep 17 00:00:00 2001 From: jacob 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? } + .run { value as? Map? } .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 { val source = TaskCompletionSource() @@ -261,19 +251,10 @@ class FirebaseAuth constructor( setResult: (responseBody: String) -> FirebaseUserImpl?, ): TaskCompletionSource { val source = TaskCompletionSource() -<<<<<<< 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 { -<<<<<<< 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 { -<<<<<<< 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 { + private fun updateByGetAccountInfo(): Task { val source = TaskCompletionSource() val body = @@ -496,18 +436,6 @@ class FirebaseAuth constructor( return source.task } -<<<<<<< HEAD - fun createUserWithEmailAndPassword(email: String, password: String): Task { - 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 { - 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 = TODO() fun signInWithCredential(authCredential: AuthCredential): Task = TODO() -======= - fun sendPasswordResetEmail( - email: String, - settings: ActionCodeSettings?, - ): Task = TODO() - ->>>>>>> 25efe1c (ktlint formatting + update of user data after signInWithCustomToken + photoUrl and displayName implementation) fun checkActionCode(code: String): Task = TODO() fun confirmPasswordReset( @@ -957,14 +851,4 @@ class FirebaseAuth constructor( ): Task = 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 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 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 { val source = TaskCompletionSource() 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 { 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 { val source = TaskCompletionSource() 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 { 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 { 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 refreshToken( user: FirebaseUserImpl, source: TaskCompletionSource, - 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 = TODO() fun fetchSignInMethodsForEmail(email: String): Task = TODO() fun sendSignInLinkToEmail( email: String, - actionCodeSettings: ActionCodeSettings, + actionCodeSettings: ActionCodeSettings ): Task = TODO() fun verifyPasswordResetCode(code: String): Task = TODO() @@ -847,7 +847,7 @@ class FirebaseAuth constructor( fun signInWithEmailLink( email: String, - link: String, + link: String ): Task = 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 abstract fun updateEmail(email: String): Task 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