Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-05 13:39:53 +04:00
parent 0b3914cf6a
commit 7581e22ee7
19 changed files with 784 additions and 74 deletions

View file

@ -89,19 +89,19 @@ dependencies {
/** Coroutines */
implementation(deps.kotlin.coroutines)
implementation(deps.kotlin.coroutines.rx2)
implementation(deps.kotlin.datetime)
implementation(deps.kotlin.serialization)
api(deps.kotlin.datetime)
api(deps.kotlin.serialization)
/** Logging */
/** Network */
implementation(deps.moshi)
api(deps.moshi)
implementation(deps.moshi.kotlin)
implementation(deps.moshi.adapters)
implementation(deps.moshi.adapters.ext)
implementation(deps.okHttp)
api(deps.okHttp)
implementation(deps.okHttp.prettyLogging)
implementation(deps.retrofit)
api(deps.retrofit)
implementation(deps.retrofit.moshi)
ksp(deps.moshi.kotlin.codegen)
kaptForObfuscatingVariants(deps.retrofit.response.type.keeper)
@ -120,7 +120,7 @@ dependencies {
releaseImplementation(deps.chuckerStub)
/** Local storages */
implementation(deps.androidx.datastore)
api(deps.androidx.datastore)
implementation(deps.room.runtime)
implementation(deps.room.ktx)
ksp(deps.room.compiler)

View file

@ -3,6 +3,7 @@ package com.tangem.datasource.api.auth
import com.tangem.datasource.api.auth.models.request.AuthApiRequest
import com.tangem.datasource.api.auth.models.request.NonceApiRequest
import com.tangem.datasource.api.auth.models.request.RefreshApiRequest
import com.tangem.datasource.api.auth.models.request.RegisterApiRequest
import com.tangem.datasource.api.auth.models.response.NonceApiResponse
import com.tangem.datasource.api.auth.models.response.TokenApiResponse
import com.tangem.datasource.api.common.response.ApiResponse
@ -14,6 +15,24 @@ import retrofit2.http.POST
*/
interface AuthApi {
/**
* Request device registration nonce.
*
* Generates a nonce bound to the device public key for the device registration flow.
*/
@POST("api/v1/auth/nonce/device")
suspend fun requestDeviceNonce(@Body request: NonceApiRequest): ApiResponse<NonceApiResponse>
/**
* Register device.
*
* Registers a new device using its hardware-backed public key and issues the initial
* session token pair. Called once per app install.
*/
@POST("api/v1/auth/register")
@RequiresDpopProof
suspend fun register(@Body request: RegisterApiRequest): ApiResponse<TokenApiResponse>
/**
* Request authentication nonce.
*

View file

@ -23,24 +23,4 @@ data class AuthenticationPayload(
@Json(name = "attestationToken") val attestationToken: String?,
/** Client-reported device metadata. */
@Json(name = "metadata") val metadata: DeviceMetadata,
) {
/** Device metadata collection. */
@JsonClass(generateAdapter = true)
data class DeviceMetadata(
/** Device hardware model (e.g. `iPhone 15 Pro`). */
@Json(name = "deviceModel") val deviceModel: String?,
/** Operating system (`android` / `ios`). */
@Json(name = "os") val os: String,
/** OS version string (e.g. `17.4.1`). */
@Json(name = "osVersion") val osVersion: String?,
/** Application version (e.g. `5.8.0`). */
@Json(name = "appVersion") val appVersion: String?,
/** User-Agent header (e.g. `Tangem/5.8.0 (iPhone; iOS 17.4.1; Scale/3.00)`). */
@Json(name = "userAgent") val userAgent: String?,
/** Client locale (e.g. `en-US`). */
@Json(name = "locale") val locale: String?,
/** Client timezone (e.g. `Europe/Moscow`). */
@Json(name = "timezone") val timezone: String?,
)
}
)

View file

@ -0,0 +1,26 @@
package com.tangem.datasource.api.auth.models.request
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* Client-reported device metadata, included in both [AuthenticationPayload] and [RegisterPayload].
* Mirrors the `DeviceMetadata` schema in the backend OpenAPI contract.
*/
@JsonClass(generateAdapter = true)
data class DeviceMetadata(
/** Device hardware model (e.g. `iPhone 15 Pro`). */
@Json(name = "deviceModel") val deviceModel: String?,
/** Operating system (`android` / `ios`). */
@Json(name = "os") val os: String,
/** OS version string (e.g. `17.4.1`). */
@Json(name = "osVersion") val osVersion: String?,
/** Application version (e.g. `5.8.0`). */
@Json(name = "appVersion") val appVersion: String?,
/** User-Agent header (e.g. `Tangem/5.8.0 (iPhone; iOS 17.4.1; Scale/3.00)`). */
@Json(name = "userAgent") val userAgent: String?,
/** Client locale (e.g. `en-US`). */
@Json(name = "locale") val locale: String?,
/** Client timezone (e.g. `Europe/Moscow`). */
@Json(name = "timezone") val timezone: String?,
)

View file

@ -0,0 +1,31 @@
package com.tangem.datasource.api.auth.models.request
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* Registration request registers a new device and establishes initial trust.
*
* Posted to `POST /api/v1/auth/register`; on success the server returns
* [com.tangem.datasource.api.auth.models.response.TokenApiResponse] (the initial session token pair).
*/
@JsonClass(generateAdapter = true)
data class RegisterApiRequest(
/** Signed registration payload. */
@Json(name = "payload") val payload: RegisterPayload,
/** EC signature over the registration payload, signed by the device private key (Base64). */
@Json(name = "signature") val signature: String,
)
/** Signed registration payload — the data that is signed by the device private key. */
@JsonClass(generateAdapter = true)
data class RegisterPayload(
/** Base64-encoded EC public key of the device. */
@Json(name = "devicePublicKey") val devicePublicKey: String,
/** Deciphered nonce value from the `/api/v1/auth/nonce/device` endpoint. */
@Json(name = "nonce") val nonce: String,
/** Platform attestation token (Play Integrity / App Attest). Optional; backend accepts `null`. */
@Json(name = "attestationToken") val attestationToken: String?,
/** Client-reported device metadata. */
@Json(name = "metadata") val metadata: DeviceMetadata,
)

View file

@ -103,6 +103,8 @@ object PreferencesKeys {
val IS_GOOGLE_PAY_AVAILABLE_KEY by lazy { booleanPreferencesKey(name = "isGooglePayAvailable") }
val IS_DEVICE_REGISTERED_KEY by lazy { booleanPreferencesKey(name = "isDeviceRegistered") }
val WAS_LOG_FILE_CLEARED by lazy { booleanPreferencesKey(name = "wasLogFileCleared") }
val SEED_FIRST_NOTIFICATION_SHOW_TIME by lazy { longPreferencesKey("seedFirstNotificationTime") }