Implement Firebase Google sign-in with enum-based auth contracts.
Refactor AuthService to use AuthProvider and User, add Firebase-backed auth wiring for login/manage flows, and fix app-level Google services configuration so Credential Manager sign-in works reliably. Made-with: Cursor
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
package org.db3.airmq.sdk.auth
|
||||
|
||||
import org.db3.airmq.sdk.auth.model.Auth
|
||||
import org.db3.airmq.sdk.auth.model.AuthProvider
|
||||
import org.db3.airmq.sdk.auth.model.User
|
||||
|
||||
interface AuthService {
|
||||
suspend fun getCurrentSession(): Auth?
|
||||
suspend fun getCurrentSession(): User?
|
||||
suspend fun isAuthenticated(): Boolean
|
||||
suspend fun signIn(provider: String, token: String? = null): Result<Auth>
|
||||
suspend fun signIn(provider: AuthProvider, token: String): Result<User>
|
||||
suspend fun signOut(): Result<Unit>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.db3.airmq.sdk.auth
|
||||
|
||||
import com.google.android.gms.tasks.Task
|
||||
import com.google.firebase.auth.FirebaseAuth
|
||||
import com.google.firebase.auth.FirebaseUser
|
||||
import com.google.firebase.auth.GoogleAuthProvider
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import org.db3.airmq.sdk.auth.model.AuthProvider
|
||||
import org.db3.airmq.sdk.auth.model.User
|
||||
|
||||
@Singleton
|
||||
class FirebaseAuthService @Inject constructor(
|
||||
private val firebaseAuth: FirebaseAuth
|
||||
) : AuthService {
|
||||
|
||||
override suspend fun getCurrentSession(): User? = firebaseAuth.currentUser?.toUser()
|
||||
|
||||
override suspend fun isAuthenticated(): Boolean = firebaseAuth.currentUser != null
|
||||
|
||||
override suspend fun signIn(provider: AuthProvider, token: String): Result<User> = runCatching {
|
||||
require(token.isNotBlank()) { "ID token is required for Google sign-in." }
|
||||
when (provider) {
|
||||
AuthProvider.GOOGLE -> signInWithGoogle(token)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun signOut(): Result<Unit> = runCatching {
|
||||
firebaseAuth.signOut()
|
||||
}
|
||||
|
||||
private suspend fun signInWithGoogle(token: String): User {
|
||||
val credential = GoogleAuthProvider.getCredential(token, null)
|
||||
val authResult = firebaseAuth.signInWithCredential(credential).awaitResult()
|
||||
val firebaseUser = authResult.user ?: error("Google sign-in completed without a Firebase user.")
|
||||
return firebaseUser.toUser()
|
||||
}
|
||||
|
||||
private fun FirebaseUser.toUser(): User = User(
|
||||
userId = uid,
|
||||
email = email,
|
||||
displayName = displayName,
|
||||
isAuthenticated = true
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun <T> Task<T>.awaitResult(): T = suspendCancellableCoroutine { continuation ->
|
||||
addOnSuccessListener { result ->
|
||||
continuation.resume(result)
|
||||
}
|
||||
addOnFailureListener { exception ->
|
||||
continuation.resumeWithException(exception)
|
||||
}
|
||||
addOnCanceledListener {
|
||||
continuation.cancel()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.db3.airmq.sdk.auth.model
|
||||
|
||||
enum class AuthProvider {
|
||||
GOOGLE
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.db3.airmq.sdk.auth.model
|
||||
|
||||
data class Auth(
|
||||
data class User(
|
||||
val userId: String,
|
||||
val email: String?,
|
||||
val displayName: String?,
|
||||
@@ -1,12 +1,15 @@
|
||||
package org.db3.airmq.sdk.di
|
||||
|
||||
import com.apollographql.apollo.ApolloClient
|
||||
import com.google.firebase.auth.FirebaseAuth
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
import org.db3.airmq.sdk.auth.AuthService
|
||||
import org.db3.airmq.sdk.auth.FirebaseAuthService
|
||||
import org.db3.airmq.sdk.map.MapServiceImpl
|
||||
import org.db3.airmq.sdk.map.MapService
|
||||
import org.db3.airmq.sdk.settings.SettingsService
|
||||
@@ -26,11 +29,19 @@ object SDKModule {
|
||||
.addInterceptor(ApolloLoggingInterceptor())
|
||||
.build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideFirebaseAuth(): FirebaseAuth = FirebaseAuth.getInstance()
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
abstract class SDKBindModule {
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindAuthService(impl: FirebaseAuthService): AuthService
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindMapService(impl: MapServiceImpl): MapService
|
||||
|
||||
Reference in New Issue
Block a user