Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.google.firebase.auth.FirebaseAuth.kt Maven / Gradle / Ivy
package com.google.firebase.auth
import android.util.Log
import com.google.android.gms.tasks.Task
import com.google.android.gms.tasks.TaskCompletionSource
import com.google.android.gms.tasks.Tasks
import com.google.firebase.FirebaseApp
import com.google.firebase.FirebaseException
import com.google.firebase.FirebasePlatform
import com.google.firebase.auth.internal.InternalAuthProvider
import com.google.firebase.internal.InternalTokenResult
import com.google.firebase.internal.api.FirebaseNoSignedInUserException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.booleanOrNull
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.doubleOrNull
import kotlinx.serialization.json.int
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 java.io.IOException
import java.util.Base64
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.TimeUnit
val jsonParser = Json { ignoreUnknownKeys = true }
@Serializable
class FirebaseUserImpl private constructor(
@Transient
private val app: FirebaseApp = FirebaseApp.getInstance(),
override val isAnonymous: Boolean,
override val uid: String,
val idToken: String,
val refreshToken: String,
val expiresIn: Int,
val createdAt: Long
) : 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()
)
val claims: Map by lazy {
jsonParser
.parseToJsonElement(String(Base64.getUrlDecoder().decode(idToken.split(".")[1])))
.jsonObject
.run { value as Map? }
.orEmpty()
}
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("https://www.googleapis.com/identitytoolkit/v3/relyingparty/deleteAccount?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))
}
@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)
}
}
})
return source.task
}
override fun reload(): Task {
val source = TaskCompletionSource()
FirebaseAuth.getInstance(app).refreshToken(this, source) { 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()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build()
companion object {
@JvmStatic
fun getInstance(): FirebaseAuth = getInstance(FirebaseApp.getInstance())
@JvmStatic
fun getInstance(app: FirebaseApp): FirebaseAuth = app.get(FirebaseAuth::class.java)
}
private val internalIdTokenListeners = CopyOnWriteArrayList()
private val idTokenListeners = CopyOnWriteArrayList()
private val authStateListeners = CopyOnWriteArrayList()
val currentUser: FirebaseUser?
get() = user
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 set(value) {
if (field != value) {
val prev = field
field = value
if (value == null) {
FirebasePlatform.firebasePlatform.clear(app.key)
} else {
FirebasePlatform.firebasePlatform.store(app.key, jsonParser.encodeToString(FirebaseUserImpl.serializer(), value))
}
GlobalScope.launch(Dispatchers.Main) {
if (prev?.uid != value?.uid) {
authStateListeners.forEach { l -> l.onAuthStateChanged(this@FirebaseAuth) }
}
if (prev?.idToken != value?.idToken) {
val result = InternalTokenResult(value?.idToken)
for (listener in internalIdTokenListeners) {
Log.i("FirebaseAuth", "Calling onIdTokenChanged for ${value?.uid} on listener $listener")
listener.onIdTokenChanged(result)
}
for (listener in idTokenListeners) {
listener.onIdTokenChanged(this@FirebaseAuth)
}
}
}
}
}
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))
}
@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))
}
@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 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)
)
} else {
val body = response.body()!!.use { it.string() }
val user = FirebaseUserImpl(app, jsonParser.parseToJsonElement(body).jsonObject)
refreshToken(user, source) { AuthResult { it } }
}
}
})
return source.task
}
internal fun createAuthInvalidUserException(
action: String,
request: Request,
response: Response
): FirebaseAuthInvalidUserException {
val body = response.body()!!.use { it.string() }
val jsonObject = jsonParser.parseToJsonElement(body).jsonObject
return FirebaseAuthInvalidUserException(
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"
)
}
fun signOut() {
// todo cancel token refresher
user = null
}
override fun getAccessToken(forceRefresh: Boolean): Task {
val user = user ?: return Tasks.forException(FirebaseNoSignedInUserException("Please sign in before trying to get a token."))
if (!forceRefresh && user.createdAt + user.expiresIn * 1000 - 5 * 60 * 1000 > System.currentTimeMillis()) {
// Log.i("FirebaseAuth", "returning existing token for user ${user.uid} from getAccessToken")
return Tasks.forResult(GetTokenResult(user.idToken, user.claims))
}
// Log.i("FirebaseAuth", "Refreshing access token forceRefresh=$forceRefresh createdAt=${user.createdAt} expiresIn=${user.expiresIn}")
val source = TaskCompletionSource()
refreshToken(user, source) { GetTokenResult(it.idToken, user.claims) }
return source.task
}
private var refreshSource = TaskCompletionSource().apply { setException(Exception()) }
internal fun refreshToken(user: FirebaseUserImpl, source: TaskCompletionSource, map: (user: FirebaseUserImpl) -> T?) {
refreshSource = refreshSource
.takeUnless { it.task.isComplete }
?: enqueueRefreshTokenCall(user)
refreshSource.task.addOnSuccessListener { source.setResult(map(it)) }
refreshSource.task.addOnFailureListener { source.setException(FirebaseException(it.toString(), it)) }
}
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("https://securetoken.googleapis.com/v1/token?key=" + app.options.apiKey)
.post(body)
.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 body = response.body()?.use { it.string() }
if (!response.isSuccessful) {
signOutAndThrowInvalidUserException(body.orEmpty(), "token API returned an error: $body")
} else {
jsonParser.parseToJsonElement(body!!).jsonObject.apply {
val user = FirebaseUserImpl(app, this, user.isAnonymous)
if (user.claims["aud"] != app.options.projectId) {
signOutAndThrowInvalidUserException(
user.claims.toString(),
"Project ID's do not match ${user.claims["aud"]} != ${app.options.projectId}"
)
} else {
[email protected] = user
source.setResult(user)
}
}
}
}
private fun signOutAndThrowInvalidUserException(body: String, message: String) {
signOut()
source.setException(FirebaseAuthInvalidUserException(body, message))
}
})
return source
}
override fun getUid(): String? {
return user?.uid
}
override fun addIdTokenListener(listener: com.google.firebase.auth.internal.IdTokenListener) {
internalIdTokenListeners.addIfAbsent(listener)
GlobalScope.launch(Dispatchers.Main) {
listener.onIdTokenChanged(InternalTokenResult(user?.idToken))
}
}
override fun removeIdTokenListener(listener: com.google.firebase.auth.internal.IdTokenListener) {
internalIdTokenListeners.remove(listener)
}
@Synchronized
fun addAuthStateListener(listener: AuthStateListener) {
authStateListeners.addIfAbsent(listener)
GlobalScope.launch(Dispatchers.Main) {
listener.onAuthStateChanged(this@FirebaseAuth)
}
}
@Synchronized
fun removeAuthStateListener(listener: AuthStateListener) {
authStateListeners.remove(listener)
}
@FunctionalInterface
interface AuthStateListener {
fun onAuthStateChanged(auth: FirebaseAuth)
}
@FunctionalInterface
interface IdTokenListener {
fun onIdTokenChanged(auth: FirebaseAuth)
}
fun addIdTokenListener(listener: IdTokenListener) {
idTokenListeners.addIfAbsent(listener)
GlobalScope.launch(Dispatchers.Main) {
listener.onIdTokenChanged(this@FirebaseAuth)
}
}
fun removeIdTokenListener(listener: IdTokenListener) {
idTokenListeners.remove(listener)
}
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()
fun fetchSignInMethodsForEmail(email: String): 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 setLanguageCode(value: String): Nothing = TODO()
fun useEmulator(host: String, port: Int): Unit = TODO()
}