mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-23 10:11:52 +03:00
use kotlinx-serialization with OATH Model
This commit is contained in:
parent
054cbb200a
commit
1d65334072
@ -13,11 +13,13 @@ if (flutterRoot == null) {
|
|||||||
|
|
||||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
if (flutterVersionCode == null) {
|
if (flutterVersionCode == null) {
|
||||||
|
//noinspection GroovyUnusedAssignment
|
||||||
flutterVersionCode = '1'
|
flutterVersionCode = '1'
|
||||||
}
|
}
|
||||||
|
|
||||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||||
if (flutterVersionName == null) {
|
if (flutterVersionName == null) {
|
||||||
|
//noinspection GroovyUnusedAssignment
|
||||||
flutterVersionName = '1.0'
|
flutterVersionName = '1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +28,7 @@ def authenticatorVersionName = "6.0.0-alpha.2"
|
|||||||
|
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: "org.jetbrains.kotlin.plugin.serialization"
|
apply plugin: 'kotlinx-serialization'
|
||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@ -80,6 +82,7 @@ dependencies {
|
|||||||
api "com.yubico.yubikit:oath:$project.yubiKitVersion"
|
api "com.yubico.yubikit:oath:$project.yubiKitVersion"
|
||||||
api "com.yubico.yubikit:support:$project.yubiKitVersion"
|
api "com.yubico.yubikit:support:$project.yubiKitVersion"
|
||||||
|
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0-RC"
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2'
|
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2'
|
||||||
|
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
43
android/app/proguard-rules.pro
vendored
43
android/app/proguard-rules.pro
vendored
@ -5,4 +5,45 @@
|
|||||||
# For more details, see
|
# For more details, see
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
-dontwarn edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
-dontwarn edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||||
|
|
||||||
|
|
||||||
|
# Keep `Companion` object fields of serializable classes.
|
||||||
|
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
|
||||||
|
-if @kotlinx.serialization.Serializable class **
|
||||||
|
-keepclassmembers class <1> {
|
||||||
|
static <1>$Companion Companion;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Keep `serializer()` on companion objects (both default and named) of serializable classes.
|
||||||
|
-if @kotlinx.serialization.Serializable class ** {
|
||||||
|
static **$* *;
|
||||||
|
}
|
||||||
|
-keepclassmembers class <2>$<3> {
|
||||||
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Keep `INSTANCE.serializer()` of serializable objects.
|
||||||
|
-if @kotlinx.serialization.Serializable class ** {
|
||||||
|
public static ** INSTANCE;
|
||||||
|
}
|
||||||
|
-keepclassmembers class <1> {
|
||||||
|
public static <1> INSTANCE;
|
||||||
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
|
||||||
|
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
|
||||||
|
|
||||||
|
# Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`.
|
||||||
|
# If you have any, uncomment and replace classes with those containing named companion objects.
|
||||||
|
#-keepattributes InnerClasses # Needed for `getDeclaredClasses`.
|
||||||
|
#-if @kotlinx.serialization.Serializable class
|
||||||
|
#com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions.
|
||||||
|
#com.example.myapplication.HasNamedCompanion2
|
||||||
|
#{
|
||||||
|
# static **$* *;
|
||||||
|
#}
|
||||||
|
#-keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept.
|
||||||
|
# static <1>$$serializer INSTANCE;
|
||||||
|
#}
|
@ -2,19 +2,7 @@ package com.yubico.authenticator.oath
|
|||||||
|
|
||||||
import com.yubico.yubikit.oath.Code
|
import com.yubico.yubikit.oath.Code
|
||||||
import com.yubico.yubikit.oath.Credential
|
import com.yubico.yubikit.oath.Credential
|
||||||
import com.yubico.yubikit.oath.OathSession
|
|
||||||
import com.yubico.yubikit.oath.OathType
|
import com.yubico.yubikit.oath.OathType
|
||||||
import kotlinx.serialization.json.JsonObject
|
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
|
||||||
|
|
||||||
fun OathSession.toJson(remembered: Boolean) = JsonObject(
|
|
||||||
mapOf(
|
|
||||||
"deviceId" to JsonPrimitive(deviceId),
|
|
||||||
"hasKey" to JsonPrimitive(isAccessKeySet),
|
|
||||||
"remembered" to JsonPrimitive(remembered),
|
|
||||||
"locked" to JsonPrimitive(isLocked)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fun ByteArray.asString() = joinToString(
|
fun ByteArray.asString() = joinToString(
|
||||||
separator = ""
|
separator = ""
|
||||||
@ -36,8 +24,8 @@ fun Credential.model(deviceId: String) = Model.Credential(
|
|||||||
|
|
||||||
fun Code.model() = Model.Code(
|
fun Code.model() = Model.Code(
|
||||||
value,
|
value,
|
||||||
validFrom,
|
validFrom / 1000,
|
||||||
validUntil
|
validUntil / 1000
|
||||||
)
|
)
|
||||||
|
|
||||||
fun Map<Credential, Code?>.model(deviceId: String): Map<Model.Credential, Model.Code?> =
|
fun Map<Credential, Code?>.model(deviceId: String): Map<Model.Credential, Model.Code?> =
|
||||||
|
@ -1,23 +1,48 @@
|
|||||||
package com.yubico.authenticator.oath
|
package com.yubico.authenticator.oath
|
||||||
|
|
||||||
import kotlinx.serialization.json.JsonArray
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.json.JsonNull
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
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
|
||||||
|
|
||||||
class Model {
|
class Model {
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Session(
|
||||||
|
@SerialName("device_id")
|
||||||
|
val deviceId: String = "",
|
||||||
|
@SerialName("has_key")
|
||||||
|
val isAccessKeySet: Boolean = false,
|
||||||
|
@SerialName("remembered")
|
||||||
|
val isRemembered: Boolean = false,
|
||||||
|
@SerialName("locked")
|
||||||
|
val isLocked: Boolean = false,
|
||||||
|
@SerialName("keystore")
|
||||||
|
val keystoreState: String = "unknown"
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable(with = OathTypeSerializer::class)
|
||||||
enum class OathType(val value: Byte) {
|
enum class OathType(val value: Byte) {
|
||||||
TOTP(0x20), HOTP(0x10)
|
TOTP(0x20),
|
||||||
|
HOTP(0x10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class Credential(
|
data class Credential(
|
||||||
|
@SerialName("device_id")
|
||||||
val deviceId: String,
|
val deviceId: String,
|
||||||
val id: String,
|
val id: String,
|
||||||
|
@SerialName("oath_type")
|
||||||
val oathType: OathType,
|
val oathType: OathType,
|
||||||
val period: Int,
|
val period: Int,
|
||||||
val issuer: String? = null,
|
val issuer: String? = null,
|
||||||
|
@SerialName("name")
|
||||||
val accountName: String,
|
val accountName: String,
|
||||||
|
@SerialName("touch_required")
|
||||||
val touchRequired: Boolean
|
val touchRequired: Boolean
|
||||||
) {
|
) {
|
||||||
override fun equals(other: Any?): Boolean =
|
override fun equals(other: Any?): Boolean =
|
||||||
@ -30,82 +55,62 @@ class Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Code(
|
@Serializable
|
||||||
|
class Code(
|
||||||
val value: String? = null,
|
val value: String? = null,
|
||||||
|
@SerialName("valid_from")
|
||||||
val validFrom: Long,
|
val validFrom: Long,
|
||||||
val validUntil: Long
|
@SerialName("valid_to")
|
||||||
|
val validTo: Long
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class CredentialWithCode(
|
data class CredentialWithCode(
|
||||||
val credential: Credential,
|
val credential: Credential,
|
||||||
val code: Code?
|
val code: Code?
|
||||||
)
|
)
|
||||||
|
|
||||||
|
object OathTypeSerializer : KSerializer<OathType> {
|
||||||
|
override fun deserialize(decoder: Decoder): OathType =
|
||||||
|
when (decoder.decodeByte()) {
|
||||||
|
OathType.HOTP.value -> OathType.HOTP
|
||||||
|
else -> OathType.TOTP
|
||||||
|
}
|
||||||
|
|
||||||
|
override val descriptor: SerialDescriptor =
|
||||||
|
PrimitiveSerialDescriptor("OathType", PrimitiveKind.BYTE)
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: OathType) {
|
||||||
|
encoder.encodeByte(value = value.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun Credential.isInteractive(): Boolean {
|
fun Credential.isInteractive(): Boolean {
|
||||||
return oathType == OathType.HOTP || (oathType == OathType.TOTP && touchRequired)
|
return oathType == OathType.HOTP || (oathType == OathType.TOTP && touchRequired)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Code.toJson() = JsonObject(
|
|
||||||
mapOf(
|
|
||||||
"value" to JsonPrimitive(value),
|
|
||||||
"valid_from" to JsonPrimitive(validFrom / 1000),
|
|
||||||
"valid_to" to JsonPrimitive(validUntil / 1000)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fun Credential.toJson() = JsonObject(
|
|
||||||
mapOf(
|
|
||||||
"id" to JsonPrimitive(id),
|
|
||||||
"device_id" to JsonPrimitive(deviceId),
|
|
||||||
"issuer" to JsonPrimitive(issuer),
|
|
||||||
"name" to JsonPrimitive(accountName),
|
|
||||||
"oath_type" to JsonPrimitive(oathType.value),
|
|
||||||
"period" to JsonPrimitive(period),
|
|
||||||
"touch_required" to JsonPrimitive(touchRequired),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fun Pair<Credential, Code?>.toJson() = JsonObject(
|
|
||||||
mapOf(
|
|
||||||
"credential" to first.toJson(),
|
|
||||||
"code" to (second?.toJson() ?: JsonNull)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fun CredentialWithCode.toJson() = JsonObject(
|
|
||||||
mapOf(
|
|
||||||
"credential" to credential.toJson(),
|
|
||||||
"code" to (code?.toJson() ?: JsonNull)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fun Map<Credential, Code?>.toJson() = JsonObject(
|
|
||||||
mapOf(
|
|
||||||
"entries" to JsonArray(
|
|
||||||
map { it.toPair().toJson() }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var deviceId: String = ""; private set
|
//var deviceId: String = ""; private set
|
||||||
|
var session = Session()
|
||||||
var credentials = mutableMapOf<Credential, Code?>(); private set
|
var credentials = mutableMapOf<Credential, Code?>(); private set
|
||||||
|
|
||||||
// resets the model to initial values
|
// resets the model to initial values
|
||||||
// used when a usb key has been disconnected
|
// used when a usb key has been disconnected
|
||||||
fun reset() {
|
fun reset() {
|
||||||
this.credentials.clear()
|
this.credentials.clear()
|
||||||
this.deviceId = ""
|
this.session = Session()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun update(deviceId: String, credentials: Map<Credential, Code?>) {
|
fun update(deviceId: String, credentials: Map<Credential, Code?>) {
|
||||||
if (this.deviceId != deviceId) {
|
if (this.session.deviceId != deviceId) {
|
||||||
// device was changed, we use the new list
|
// device was changed, we use the new list
|
||||||
this.credentials.clear()
|
this.credentials.clear()
|
||||||
this.credentials.putAll(from = credentials)
|
this.credentials.putAll(from = credentials)
|
||||||
this.deviceId = deviceId
|
this.session = Session(deviceId)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// update codes for non interactive keys
|
// update codes for non interactive keys
|
||||||
@ -126,7 +131,7 @@ class Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun add(deviceId: String, credential: Credential, code: Code?): CredentialWithCode? {
|
fun add(deviceId: String, credential: Credential, code: Code?): CredentialWithCode? {
|
||||||
if (this.deviceId != deviceId) {
|
if (this.session.deviceId != deviceId) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +145,7 @@ class Model {
|
|||||||
oldCredential: Credential,
|
oldCredential: Credential,
|
||||||
newCredential: Credential
|
newCredential: Credential
|
||||||
): Credential? {
|
): Credential? {
|
||||||
if (this.deviceId != deviceId) {
|
if (this.session.deviceId != deviceId) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +167,7 @@ class Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateCode(deviceId: String, credential: Credential, code: Code?): Code? {
|
fun updateCode(deviceId: String, credential: Credential, code: Code?): Code? {
|
||||||
if (this.deviceId != deviceId) {
|
if (this.session.deviceId != deviceId) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import androidx.lifecycle.Observer
|
|||||||
import com.yubico.authenticator.*
|
import com.yubico.authenticator.*
|
||||||
import com.yubico.authenticator.api.Pigeon.*
|
import com.yubico.authenticator.api.Pigeon.*
|
||||||
import com.yubico.authenticator.data.device.toJson
|
import com.yubico.authenticator.data.device.toJson
|
||||||
import com.yubico.authenticator.oath.Model.Companion.toJson
|
|
||||||
import com.yubico.authenticator.oath.keystore.ClearingMemProvider
|
import com.yubico.authenticator.oath.keystore.ClearingMemProvider
|
||||||
import com.yubico.authenticator.oath.keystore.KeyStoreProvider
|
import com.yubico.authenticator.oath.keystore.KeyStoreProvider
|
||||||
import com.yubico.yubikit.android.transport.nfc.NfcYubiKeyDevice
|
import com.yubico.yubikit.android.transport.nfc.NfcYubiKeyDevice
|
||||||
@ -19,6 +18,8 @@ import com.yubico.yubikit.oath.*
|
|||||||
import com.yubico.yubikit.support.DeviceUtil
|
import com.yubico.yubikit.support.DeviceUtil
|
||||||
import io.flutter.plugin.common.BinaryMessenger
|
import io.flutter.plugin.common.BinaryMessenger
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
@ -33,6 +34,12 @@ class OathManager(
|
|||||||
private val dialogManager: DialogManager
|
private val dialogManager: DialogManager
|
||||||
) : OathApi {
|
) : OathApi {
|
||||||
|
|
||||||
|
// application specific Json settings
|
||||||
|
private val json = Json {
|
||||||
|
allowStructuredMapKeys = true
|
||||||
|
encodeDefaults = true
|
||||||
|
}
|
||||||
|
|
||||||
private val _dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
private val _dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||||
private val coroutineScope = CoroutineScope(SupervisorJob() + _dispatcher)
|
private val coroutineScope = CoroutineScope(SupervisorJob() + _dispatcher)
|
||||||
|
|
||||||
@ -152,7 +159,7 @@ class OathManager(
|
|||||||
}
|
}
|
||||||
if (response.isUnlocked == true) {
|
if (response.isUnlocked == true) {
|
||||||
_model.update(it.deviceId, calculateOathCodes(it).model(it.deviceId))
|
_model.update(it.deviceId, calculateOathCodes(it).model(it.deviceId))
|
||||||
codes = _model.credentials.toJson().toString()
|
codes = json.encodeToString(_model.credentials)
|
||||||
}
|
}
|
||||||
returnSuccess(result, response)
|
returnSuccess(result, response)
|
||||||
}
|
}
|
||||||
@ -253,7 +260,7 @@ class OathManager(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (addedCred != null) {
|
if (addedCred != null) {
|
||||||
val jsonResult = addedCred.toJson().toString()
|
val jsonResult = json.encodeToString(addedCred)
|
||||||
returnSuccess(result, jsonResult)
|
returnSuccess(result, jsonResult)
|
||||||
} else {
|
} else {
|
||||||
// TODO - figure out better error handling here
|
// TODO - figure out better error handling here
|
||||||
@ -281,7 +288,8 @@ class OathManager(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (renamedCredential != null) {
|
if (renamedCredential != null) {
|
||||||
val jsonResult = renamedCredential.toJson().toString()
|
val jsonResult =
|
||||||
|
json.encodeToString(renamedCredential)
|
||||||
|
|
||||||
returnSuccess(result, jsonResult)
|
returnSuccess(result, jsonResult)
|
||||||
} else {
|
} else {
|
||||||
@ -322,7 +330,7 @@ class OathManager(
|
|||||||
session.deviceId,
|
session.deviceId,
|
||||||
calculateOathCodes(session).model(session.deviceId)
|
calculateOathCodes(session).model(session.deviceId)
|
||||||
)
|
)
|
||||||
val resultJson = _model.credentials.toJson().toString()
|
val resultJson = json.encodeToString(_model.credentials)
|
||||||
returnSuccess(result, resultJson)
|
returnSuccess(result, resultJson)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -347,7 +355,7 @@ class OathManager(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (code != null) {
|
if (code != null) {
|
||||||
val resultJson = code.toJson().toString()
|
val resultJson = json.encodeToString(code)
|
||||||
|
|
||||||
returnSuccess(result, resultJson)
|
returnSuccess(result, resultJson)
|
||||||
} else {
|
} else {
|
||||||
@ -426,9 +434,14 @@ class OathManager(
|
|||||||
tryToUnlockOathSession(oathSession)
|
tryToUnlockOathSession(oathSession)
|
||||||
val isRemembered = _keyManager.isRemembered(oathSession.deviceId)
|
val isRemembered = _keyManager.isRemembered(oathSession.deviceId)
|
||||||
|
|
||||||
val oathSessionData = oathSession
|
_model.session = Model.Session(
|
||||||
.toJson(isRemembered)
|
oathSession.deviceId,
|
||||||
.toString()
|
oathSession.isAccessKeySet,
|
||||||
|
isRemembered,
|
||||||
|
oathSession.isLocked
|
||||||
|
)
|
||||||
|
|
||||||
|
val oathSessionData = json.encodeToString(_model.session)
|
||||||
it.resume(oathSessionData)
|
it.resume(oathSessionData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -445,7 +458,7 @@ class OathManager(
|
|||||||
session.deviceId,
|
session.deviceId,
|
||||||
calculateOathCodes(session).model(session.deviceId)
|
calculateOathCodes(session).model(session.deviceId)
|
||||||
)
|
)
|
||||||
val resultJson = _model.credentials.toJson().toString()
|
val resultJson = json.encodeToString(_model.credentials)
|
||||||
it.resume(resultJson)
|
it.resume(resultJson)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.6.10'
|
ext.kotlin_version = '1.6.20'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@ -8,7 +8,7 @@ buildscript {
|
|||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.1.3'
|
classpath 'com.android.tools.build:gradle:7.1.3'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-serialization:1.6.10"
|
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,11 +14,7 @@ class _StateProvider extends StateNotifier<OathState?> {
|
|||||||
|
|
||||||
void setFromString(String input) {
|
void setFromString(String input) {
|
||||||
var resultJson = jsonDecode(input);
|
var resultJson = jsonDecode(input);
|
||||||
state = OathState(resultJson['deviceId'],
|
state = OathState.fromJson(resultJson);
|
||||||
hasKey: resultJson['hasKey'],
|
|
||||||
remembered: resultJson['remembered'],
|
|
||||||
locked: resultJson['locked'],
|
|
||||||
keystore: KeystoreState.unknown);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,11 +29,21 @@ class _CredentialsProvider extends StateNotifier<List<OathPair>?> {
|
|||||||
void setFromString(String input) {
|
void setFromString(String input) {
|
||||||
var result = jsonDecode(input);
|
var result = jsonDecode(input);
|
||||||
|
|
||||||
|
/// structure of data in the json object is:
|
||||||
|
/// [credential1, code1, credential2, code2, ...]
|
||||||
|
|
||||||
final List<OathPair> pairs = [];
|
final List<OathPair> pairs = [];
|
||||||
for (var e in result['entries']) {
|
if (result is List<dynamic>) {
|
||||||
final credential = OathCredential.fromJson(e['credential']);
|
for (var index = 0; index < result.length / 2; index++) {
|
||||||
final code = e['code'] == null ? null : OathCode.fromJson(e['code']);
|
final credential = result[index * 2];
|
||||||
pairs.add(OathPair(credential, code));
|
final code = result[index * 2 + 1];
|
||||||
|
pairs.add(
|
||||||
|
OathPair(
|
||||||
|
OathCredential.fromJson(credential),
|
||||||
|
code == null ? null : OathCode.fromJson(code),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state = pairs;
|
state = pairs;
|
||||||
|
Loading…
Reference in New Issue
Block a user