Automatic login with JWT and Logout
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
304508fa8c
commit
b575f6e157
@ -0,0 +1,17 @@
|
|||||||
|
package fr.iut.alldev.allin.di
|
||||||
|
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import fr.iut.alldev.allin.keystore.AllInKeystoreManager
|
||||||
|
import fr.iut.alldev.allin.keystore.impl.AllInKeystoreManagerImpl
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
abstract class KeystoreModule {
|
||||||
|
@Singleton
|
||||||
|
@Binds
|
||||||
|
abstract fun provideKeystoreManager(allInKeystoreManagerImpl: AllInKeystoreManagerImpl): AllInKeystoreManager
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package fr.iut.alldev.allin.keystore
|
||||||
|
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
|
|
||||||
|
abstract class AllInKeystoreManager {
|
||||||
|
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
|
||||||
|
|
||||||
|
abstract fun createKeystore()
|
||||||
|
abstract fun putToken(token: String)
|
||||||
|
abstract fun getToken(): String?
|
||||||
|
abstract fun deleteToken()
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package fr.iut.alldev.allin.keystore.impl
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import fr.iut.alldev.allin.keystore.AllInKeystoreManager
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private const val AUTH_TOKEN_KEY = "auth_token"
|
||||||
|
private const val PREFS_FILE_NAME = "secured_shared_prefs"
|
||||||
|
|
||||||
|
class AllInKeystoreManagerImpl @Inject constructor(
|
||||||
|
@ApplicationContext private val context: Context,
|
||||||
|
) : AllInKeystoreManager() {
|
||||||
|
|
||||||
|
private var sharedPreferences: SharedPreferences? = null
|
||||||
|
override fun createKeystore() {
|
||||||
|
if (sharedPreferences == null) {
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
PREFS_FILE_NAME,
|
||||||
|
masterKeyAlias,
|
||||||
|
context,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun putToken(token: String) {
|
||||||
|
sharedPreferences?.edit()?.putString(AUTH_TOKEN_KEY, token)?.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun getToken(): String? {
|
||||||
|
return sharedPreferences?.getString(AUTH_TOKEN_KEY, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deleteToken() {
|
||||||
|
sharedPreferences?.edit()?.putString(AUTH_TOKEN_KEY, null)?.apply()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package fr.iut.alldev.allin.ui.welcome
|
||||||
|
|
||||||
|
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import fr.iut.alldev.allin.data.repository.UserRepository
|
||||||
|
import fr.iut.alldev.allin.keystore.AllInKeystoreManager
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class WelcomeScreenViewModel @Inject constructor(
|
||||||
|
private val keystoreManager: AllInKeystoreManager,
|
||||||
|
private val userRepository: UserRepository
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
var loading = mutableStateOf(false)
|
||||||
|
|
||||||
|
fun tryAutoLogin(onSuccess: () -> Unit) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
loading.value = true
|
||||||
|
keystoreManager.createKeystore()
|
||||||
|
keystoreManager.getToken()?.let { token ->
|
||||||
|
runCatching {
|
||||||
|
userRepository
|
||||||
|
.login(token)
|
||||||
|
?.let { newToken ->
|
||||||
|
keystoreManager.putToken(newToken)
|
||||||
|
}
|
||||||
|
onSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package fr.iut.alldev.allin.data.api.model
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import fr.iut.alldev.allin.data.model.bet.Bet
|
||||||
|
import fr.iut.alldev.allin.data.model.bet.BetStatus
|
||||||
|
import fr.iut.alldev.allin.data.model.bet.CustomBet
|
||||||
|
import fr.iut.alldev.allin.data.model.bet.YesNoBet
|
||||||
|
import fr.iut.alldev.allin.data.serialization.ZonedDateTimeSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@Serializable
|
||||||
|
data class ResponseBet(
|
||||||
|
val id: Int?,
|
||||||
|
val theme: String,
|
||||||
|
val sentenceBet: String,
|
||||||
|
@Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime,
|
||||||
|
@Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime,
|
||||||
|
var isPrivate: Boolean,
|
||||||
|
var response: List<String>,
|
||||||
|
val createdBy: String,
|
||||||
|
) {
|
||||||
|
fun toBet(): Bet {
|
||||||
|
if (response.toSet() == setOf("Yes", "No")) {
|
||||||
|
return YesNoBet(
|
||||||
|
theme = theme,
|
||||||
|
phrase = sentenceBet,
|
||||||
|
endRegisterDate = endRegistration,
|
||||||
|
endBetDate = endBet,
|
||||||
|
isPublic = !isPrivate,
|
||||||
|
betStatus = BetStatus.Waiting,
|
||||||
|
creator = createdBy
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return CustomBet(
|
||||||
|
theme = theme,
|
||||||
|
phrase = sentenceBet,
|
||||||
|
endRegisterDate = endRegistration,
|
||||||
|
endBetDate = endBet,
|
||||||
|
isPublic = !isPrivate,
|
||||||
|
betStatus = BetStatus.Waiting,
|
||||||
|
creator = createdBy,
|
||||||
|
possibleAnswers = response.toSet()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,29 @@
|
|||||||
package fr.iut.alldev.allin.data.model.bet
|
package fr.iut.alldev.allin.data.model.bet
|
||||||
|
|
||||||
|
import fr.iut.alldev.allin.data.api.model.ResponseBet
|
||||||
import java.time.ZonedDateTime
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
abstract class Bet(
|
abstract class Bet(
|
||||||
|
open val id: Int? = null,
|
||||||
|
open val creator: String,
|
||||||
open val theme: String,
|
open val theme: String,
|
||||||
open val phrase: String,
|
open val phrase: String,
|
||||||
open val endRegisterDate: ZonedDateTime,
|
open val endRegisterDate: ZonedDateTime,
|
||||||
open val endBetDate: ZonedDateTime,
|
open val endBetDate: ZonedDateTime,
|
||||||
open val isPublic: Boolean,
|
open val isPublic: Boolean,
|
||||||
open val betStatus: BetStatus,
|
open val betStatus: BetStatus,
|
||||||
|
) {
|
||||||
|
abstract fun getResponses(): List<String>
|
||||||
|
fun toResponseBet(): ResponseBet {
|
||||||
|
return ResponseBet(
|
||||||
|
id = id,
|
||||||
|
theme = theme,
|
||||||
|
sentenceBet = phrase,
|
||||||
|
endRegistration = endRegisterDate,
|
||||||
|
endBet = endBetDate,
|
||||||
|
isPrivate = !isPublic,
|
||||||
|
response = getResponses(),
|
||||||
|
createdBy = creator
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package fr.iut.alldev.allin.data.serialization
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.ZoneId
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
object ZonedDateTimeSerializer : KSerializer<ZonedDateTime> {
|
||||||
|
override val descriptor: SerialDescriptor =
|
||||||
|
PrimitiveSerialDescriptor("ZonedDateTime", PrimitiveKind.LONG)
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: ZonedDateTime) {
|
||||||
|
encoder.encodeLong(value.toEpochSecond())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): ZonedDateTime {
|
||||||
|
val epoch = decoder.decodeLong()
|
||||||
|
return ZonedDateTime.ofInstant(Instant.ofEpochSecond(epoch), ZoneId.systemDefault())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue