Updated on 2026-08-14

This commit is contained in:
Tangem 2025-01-14 13:45:57 +03:00
parent 113c9cb46e
commit 4192ccab33
34 changed files with 444 additions and 81 deletions

View file

@ -0,0 +1,54 @@
package com.tangem.tap.data
import android.content.Context
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import com.tangem.common.services.secure.SecureStorage
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
import com.tangem.domain.visa.model.VisaAuthTokens
import com.tangem.sdk.storage.AndroidSecureStorage
import com.tangem.sdk.storage.createEncryptedSharedPreferences
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
internal class DefaultVisaAuthTokenStorage @Inject constructor(
@ApplicationContext applicationContext: Context,
private val dispatcherProvider: CoroutineDispatcherProvider,
) : VisaAuthTokenStorage {
private val secureStorage = AndroidSecureStorage(
preferences = SecureStorage.createEncryptedSharedPreferences(
context = applicationContext,
storageName = "visa_auth_storage",
),
)
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val tokensAdapter = moshi.adapter(VisaAuthTokens::class.java)
override suspend fun store(tokens: VisaAuthTokens) = withContext(dispatcherProvider.io) {
val json = tokensAdapter.toJson(tokens)
secureStorage.store(
json.encodeToByteArray(throwOnInvalidSequence = true),
VISA_AUTH_TOKENS_KEY,
)
}
override suspend fun get(): VisaAuthTokens? = withContext(dispatcherProvider.io) {
secureStorage.get(VISA_AUTH_TOKENS_KEY)
?.decodeToString(throwOnInvalidSequence = true)
?.let(tokensAdapter::fromJson)
}
private companion object {
const val VISA_AUTH_TOKENS_KEY = "visa_auth_tokens"
}
}

View file

@ -0,0 +1,18 @@
package com.tangem.tap.di.data
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
import com.tangem.tap.data.DefaultVisaAuthTokenStorage
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal interface VisaStorageModule {
@Binds
@Singleton
fun bindVisaStorage(impl: DefaultVisaAuthTokenStorage): VisaAuthTokenStorage
}

View file

@ -7,11 +7,10 @@ import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.toHexString
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
import com.tangem.domain.common.visa.VisaUtilities
import com.tangem.domain.visa.model.VisaActivationInput
import com.tangem.domain.visa.model.VisaAuthSignedChallenge
import com.tangem.domain.visa.model.VisaCardActivationStatus
import com.tangem.domain.visa.model.toSignedChallenge
import com.tangem.domain.visa.model.*
import com.tangem.domain.visa.repository.VisaActivationRepository
import com.tangem.domain.visa.repository.VisaAuthRepository
import com.tangem.operations.attestation.AttestCardKeyCommand
import com.tangem.operations.attestation.AttestCardKeyResponse
@ -25,6 +24,8 @@ import kotlin.coroutines.resume
internal class VisaCardScanHandler @Inject constructor(
private val visaAuthRepository: VisaAuthRepository,
private val visaActivationRepository: VisaActivationRepository,
private val visaAuthTokenStorage: VisaAuthTokenStorage,
) {
suspend fun handleVisaCardScan(session: CardSession): CompletionResult<VisaCardActivationStatus> {
@ -153,6 +154,8 @@ internal class VisaCardScanHandler @Inject constructor(
return handleCardAuthorization(session)
}
visaAuthTokenStorage.store(authorizationTokensResponse)
Timber.i("Authorized using Wallet public key successfully")
return CompletionResult.Success(VisaCardActivationStatus.Activated(authorizationTokensResponse))
@ -207,7 +210,33 @@ internal class VisaCardScanHandler @Inject constructor(
)
}
TODO() // implement card activation status handling ([REDACTED_TASK_KEY])
visaAuthTokenStorage.store(authorizationTokensResponse)
val activationRemoteState = visaActivationRepository.getActivationRemoteState()
val error = when (activationRemoteState) {
VisaActivationRemoteState.BlockedForActivation -> VisaActivationError.BlockedForActivation
VisaActivationRemoteState.Activated -> VisaActivationError.InvalidActivationState
else -> null
}
if (error != null) {
return CompletionResult.Failure(TangemSdkError.Underlying(error.message))
}
val activationInput = VisaActivationInput(
cardId = card.cardId,
cardPublicKey = card.cardPublicKey,
isAccessCodeSet = card.isAccessCodeSet,
)
return CompletionResult.Success(
VisaCardActivationStatus.ActivationStarted(
activationInput = activationInput,
authTokens = authorizationTokensResponse,
remoteState = activationRemoteState,
),
)
}
private suspend fun signChallengeWithWallet(

View file

@ -0,0 +1,14 @@
package com.tangem.tap.network.auth
import com.tangem.datasource.api.common.visa.TangemVisaAuthProvider
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
import javax.inject.Inject
internal class DefaultVisaAuthProvider @Inject constructor(
private val authStorage: VisaAuthTokenStorage,
) : TangemVisaAuthProvider {
override suspend fun getAuthHeader(): String {
return authStorage.get()?.accessToken?.let { "Bearer $it" } ?: "Error in the app!"
}
}

View file

@ -1,17 +1,21 @@
package com.tangem.tap.network.auth.di
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.api.common.visa.TangemVisaAuthProvider
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.lib.auth.ExpressAuthProvider
import com.tangem.lib.auth.StakeKitAuthProvider
import com.tangem.tap.network.auth.*
import com.tangem.tap.network.auth.DefaultAppVersionProvider
import com.tangem.tap.network.auth.DefaultAuthProvider
import com.tangem.tap.network.auth.DefaultExpressAuthProvider
import com.tangem.tap.network.auth.DefaultStakeKitAuthProvider
import com.tangem.tap.network.auth.DefaultVisaAuthProvider
import com.tangem.utils.version.AppVersionProvider
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -20,7 +24,7 @@ import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
class AuthModule {
internal class AuthModule {
@Provides
@Singleton
@ -51,4 +55,13 @@ class AuthModule {
fun provideAppVersionProvider(): AppVersionProvider {
return DefaultAppVersionProvider()
}
}
@Module
@InstallIn(SingletonComponent::class)
internal interface AuthBindModule {
@Binds
@Singleton
fun bindVisaAuthProvider(authStorage: DefaultVisaAuthProvider): TangemVisaAuthProvider
}