taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

Auth.kt (6153B)


      1 /*
      2  * This file is part of GNU Taler
      3  * (C) 2026 Taler Systems S.A.
      4  *
      5  * GNU Taler is free software; you can redistribute it and/or modify it under the
      6  * terms of the GNU General Public License as published by the Free Software
      7  * Foundation; either version 3, or (at your option) any later version.
      8  *
      9  * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
     10  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11  * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License along with
     14  * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15  */
     16 
     17 package net.taler.common
     18 
     19 import kotlinx.serialization.KSerializer
     20 import kotlinx.serialization.SerialName
     21 import kotlinx.serialization.Serializable
     22 import kotlinx.serialization.SerializationException
     23 import kotlinx.serialization.descriptors.SerialDescriptor
     24 import kotlinx.serialization.descriptors.buildClassSerialDescriptor
     25 import kotlinx.serialization.descriptors.element
     26 import kotlinx.serialization.encoding.Decoder
     27 import kotlinx.serialization.encoding.Encoder
     28 import kotlinx.serialization.json.JsonDecoder
     29 import kotlinx.serialization.json.JsonElement
     30 import kotlinx.serialization.json.JsonEncoder
     31 import kotlinx.serialization.json.JsonObject
     32 import kotlinx.serialization.json.JsonPrimitive
     33 import kotlinx.serialization.json.buildJsonObject
     34 import kotlinx.serialization.json.long
     35 import kotlinx.serialization.json.longOrNull
     36 
     37 @Serializable
     38 data class TokenSuccessResponse(
     39     val expiration: Timestamp,
     40 
     41     @SerialName("access_token")
     42     val accessToken: String,
     43 )
     44 
     45 @Serializable
     46 data class ChallengesResponse(
     47     val challenges: List<Challenge>,
     48 
     49     @SerialName("combi_and")
     50     val combiAnd: Boolean,
     51 )
     52 
     53 @Serializable
     54 enum class TanChannel {
     55     @SerialName("sms")
     56     SMS,
     57 
     58     @SerialName("email")
     59     EMAIL,
     60 }
     61 
     62 @Serializable
     63 data class Challenge(
     64     @SerialName("challenge_id")
     65     val challengeId: String,
     66 
     67     @SerialName("tan_channel")
     68     val tanChannel: TanChannel,
     69 
     70     @SerialName("tan_info")
     71     val tanInfo: String,
     72 )
     73 
     74 @Serializable
     75 data class ChallengeConfirmRequest(
     76     val tan: String,
     77 )
     78 
     79 @Serializable
     80 data class TokenRequest(
     81     val scope: String,
     82     val duration: TokenDuration
     83 )
     84 
     85 @Serializable(with = TokenDuration.Serializer::class)
     86 sealed class TokenDuration {
     87     data object Forever : TokenDuration()
     88     data class Micros(val us: Long) : TokenDuration()
     89 
     90     object Serializer : KSerializer<TokenDuration> {
     91         override val descriptor: SerialDescriptor = buildClassSerialDescriptor("TokenDuration") {
     92             element<JsonElement>("d_us")
     93         }
     94 
     95         override fun serialize(encoder: Encoder, value: TokenDuration) {
     96             val jsonEncoder = encoder as? JsonEncoder
     97                 ?: throw SerializationException("Can be serialized only by JSON")
     98             val obj = when (value) {
     99                 is Forever -> buildJsonObject {
    100                     put("d_us", JsonPrimitive("forever"))
    101                 }
    102                 is Micros -> buildJsonObject {
    103                     put("d_us", JsonPrimitive(value.us))
    104                 }
    105             }
    106             jsonEncoder.encodeJsonElement(obj)
    107         }
    108 
    109         override fun deserialize(decoder: Decoder): TokenDuration {
    110             val jsonDecoder = decoder as? JsonDecoder
    111                 ?: throw SerializationException("Can be deserialized only by JSON")
    112             val element = jsonDecoder.decodeJsonElement()
    113             if (element !is JsonObject) {
    114                 throw SerializationException("Expected JSON object for TokenDuration, got: $element")
    115             }
    116             val field = element["d_us"]
    117                 ?: throw SerializationException("Missing 'd_us' field in $element")
    118             return when {
    119                 field is JsonPrimitive && field.longOrNull != null ->
    120                     Micros(field.long)
    121                 field is JsonPrimitive && field.isString && field.content == "forever" ->
    122                     Forever
    123                 else ->
    124                     throw SerializationException("Invalid 'd_us' value: $field")
    125             }
    126         }
    127     }
    128 }
    129 
    130 @Serializable(with = TokenExpiration.Serializer::class)
    131 sealed class TokenExpiration {
    132     data class Seconds(val t_s: Long) : TokenExpiration()
    133     data object Never : TokenExpiration()
    134 
    135     object Serializer : KSerializer<TokenExpiration> {
    136         override val descriptor: SerialDescriptor =
    137             buildClassSerialDescriptor("TokenExpiration") {
    138                 element<JsonElement>("t_s")
    139             }
    140 
    141         override fun serialize(encoder: Encoder, value: TokenExpiration) {
    142             val jsonEncoder = encoder as? JsonEncoder
    143                 ?: throw SerializationException("TokenExpiration can be serialized only by JSON")
    144             val obj = when (value) {
    145                 is Seconds ->
    146                     buildJsonObject { put("t_s", JsonPrimitive(value.t_s)) }
    147                 Never ->
    148                     buildJsonObject { put("t_s", JsonPrimitive("never")) }
    149             }
    150             jsonEncoder.encodeJsonElement(obj)
    151         }
    152 
    153         override fun deserialize(decoder: Decoder): TokenExpiration {
    154             val jsonDecoder = decoder as? JsonDecoder
    155                 ?: throw SerializationException("TokenExpiration can be deserialized only by JSON")
    156             val element = jsonDecoder.decodeJsonElement()
    157             if (element !is JsonObject) {
    158                 throw SerializationException("Expected JSON object for TokenExpiration, got: $element")
    159             }
    160             val field = element["t_s"]
    161                 ?: throw SerializationException("Missing 't_s' in TokenExpiration: $element")
    162 
    163             return when {
    164                 field is JsonPrimitive && field.longOrNull != null ->
    165                     Seconds(field.long)
    166                 field is JsonPrimitive && field.isString && field.content == "never" ->
    167                     Never
    168                 else ->
    169                     throw SerializationException("Invalid 't_s' value in TokenExpiration: $field")
    170             }
    171         }
    172     }
    173 }