kotlinx-serialization-json

Authentication LibraryKotlinJSONSerializationSecurityREST API

Authentication Library

kotlinx-serialization-json

Overview

kotlinx-serialization-json is Kotlin's multiplatform JSON serialization library that provides secure token handling and type-safe JSON data conversion for authentication systems.

Details

kotlinx-serialization-json is the official JSON serialization library for the Kotlin programming language, developed by JetBrains. In authentication systems, it plays a crucial role in safely handling JWT token parsing, OAuth2 response processing, user profile information serialization, and type-safe handling of JSON format data. The library provides complete compile-time type checking to prevent runtime errors. Important authentication features include ignoring unknown fields, proper null value handling, and implicit exclusion of security-critical fields. It also supports standards compliance like PKCS#7 and RFC 6750, and base64 encoding necessary for secure storage of access tokens and refresh tokens. With multiplatform support, you can use consistent APIs for both server-side and client-side authentication. The library offers excellent performance with lightweight and fast serialization processing, making it ideal for high-throughput authentication scenarios.

Pros and Cons

Pros

  • Type Safety: Secure authentication token handling with compile-time type checking
  • Security-Focused: Robustness through unknown field ignoring and null value handling
  • Multiplatform: Consistent API across JVM, Android, iOS, JS, and Native platforms
  • Performance: Lightweight and fast serialization processing
  • Standards Compliant: Full support for JWT, OAuth2, and OpenID Connect standards
  • Customizable: Adaptable to authentication provider-specific requirements
  • Type Inference Support: Enhanced development experience with Kotlin's type inference

Cons

  • Learning Curve: Requires understanding of Kotlin's annotation features and type system
  • Kotlin-Only: Limited direct usage from other JVM languages
  • Dependencies: Dependency on Kotlin ecosystem including kotlinx-coroutines
  • Configuration Complexity: Advanced customization can be complex to configure
  • Debugging Difficulty: Identifying causes of serialization errors can be challenging

Key Links

Code Examples

Hello World (Basic Token Processing)

import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class AuthToken(
    val accessToken: String,
    val tokenType: String = "Bearer",
    val expiresIn: Int,
    val refreshToken: String? = null
)

fun main() {
    val tokenJson = """
        {
            "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
            "token_type": "Bearer",
            "expires_in": 3600,
            "refresh_token": "refresh_token_here"
        }
    """
    
    val token = Json.decodeFromString<AuthToken>(tokenJson)
    println("Access Token: ${token.accessToken}")
    println("Expires In: ${token.expiresIn} seconds")
}

OAuth2 Response Processing

import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class OAuth2Response(
    val accessToken: String,
    val tokenType: String,
    val expiresIn: Int,
    val scope: String? = null,
    val refreshToken: String? = null,
    val idToken: String? = null // OpenID Connect
)

// Configuration to ignore unknown fields
val secureJson = Json {
    ignoreUnknownKeys = true
    coerceInputValues = true
    isLenient = true
}

fun processOAuth2Response(jsonResponse: String): OAuth2Response {
    return secureJson.decodeFromString<OAuth2Response>(jsonResponse)
}

fun main() {
    val oauthResponse = """
        {
            "access_token": "ya29.a0AfH6SMBqX...",
            "token_type": "Bearer",
            "expires_in": 3599,
            "scope": "openid profile email",
            "refresh_token": "1//0GWZ...",
            "id_token": "eyJhbGciOiJSUzI1NiIs...",
            "unknown_field": "ignored"
        }
    """
    
    val response = processOAuth2Response(oauthResponse)
    println("Token Type: ${response.tokenType}")
    println("Scope: ${response.scope}")
}

Secure User Profile Processing

import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class UserProfile(
    val id: String,
    val email: String,
    val name: String,
    val picture: String? = null,
    @SerialName("email_verified")
    val emailVerified: Boolean = false,
    val locale: String? = null
)

@Serializable
data class SecureUserSession(
    val userId: String,
    val email: String,
    val roles: List<String>,
    val permissions: Set<String>,
    val sessionId: String,
    val expiresAt: Long,
    @Transient // Exclude from serialization
    val sensitiveData: String? = null
)

fun main() {
    val userJson = """
        {
            "id": "user123",
            "email": "[email protected]",
            "name": "John Doe",
            "picture": "https://example.com/avatar.jpg",
            "email_verified": true,
            "locale": "en"
        }
    """
    
    val user = Json.decodeFromString<UserProfile>(userJson)
    
    // Create secure session
    val session = SecureUserSession(
        userId = user.id,
        email = user.email,
        roles = listOf("user"),
        permissions = setOf("read", "write"),
        sessionId = "session_${System.currentTimeMillis()}",
        expiresAt = System.currentTimeMillis() + 3600000
    )
    
    val sessionJson = Json.encodeToString(session)
    println("Secure Session: $sessionJson")
}

JWT Payload Processing

import kotlinx.serialization.*
import kotlinx.serialization.json.*
import java.util.Base64

@Serializable
data class JWTHeader(
    val alg: String,
    val typ: String,
    val kid: String? = null
)

@Serializable
data class JWTPayload(
    val sub: String, // Subject
    val iss: String, // Issuer
    val aud: String, // Audience
    val exp: Long,   // Expiration
    val iat: Long,   // Issued At
    val nbf: Long? = null, // Not Before
    val jti: String? = null, // JWT ID
    val scope: String? = null,
    val email: String? = null,
    val name: String? = null
)

object JWTParser {
    private val json = Json {
        ignoreUnknownKeys = true
        coerceInputValues = true
    }
    
    fun parseJWT(token: String): Pair<JWTHeader, JWTPayload> {
        val parts = token.split(".")
        require(parts.size == 3) { "Invalid JWT format" }
        
        val headerJson = Base64.getDecoder().decode(parts[0]).decodeToString()
        val payloadJson = Base64.getDecoder().decode(parts[1]).decodeToString()
        
        val header = json.decodeFromString<JWTHeader>(headerJson)
        val payload = json.decodeFromString<JWTPayload>(payloadJson)
        
        return header to payload
    }
}

fun main() {
    // Example JWT token (simplified)
    val jwtToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
    
    try {
        val (header, payload) = JWTParser.parseJWT(jwtToken)
        println("Algorithm: ${header.alg}")
        println("Subject: ${payload.sub}")
        println("Issuer: ${payload.iss}")
        println("Expires: ${payload.exp}")
    } catch (e: Exception) {
        println("JWT parsing failed: ${e.message}")
    }
}

Authentication Error Handling

import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class AuthError(
    val error: String,
    val errorDescription: String? = null,
    @SerialName("error_uri")
    val errorUri: String? = null,
    val state: String? = null
)

@Serializable
data class AuthResult<T>(
    val success: Boolean,
    val data: T? = null,
    val error: AuthError? = null
)

class AuthenticationManager {
    private val json = Json {
        ignoreUnknownKeys = true
        coerceInputValues = true
        explicitNulls = false
    }
    
    fun <T> handleAuthResponse(
        jsonResponse: String,
        dataSerializer: KSerializer<T>
    ): AuthResult<T> {
        return try {
            // Try to parse as success response
            val data = json.decodeFromJsonElement(
                dataSerializer,
                json.parseToJsonElement(jsonResponse)
            )
            AuthResult(success = true, data = data)
        } catch (e: Exception) {
            // Parse as error response
            try {
                val error = json.decodeFromString<AuthError>(jsonResponse)
                AuthResult(success = false, error = error)
            } catch (parseError: Exception) {
                AuthResult(
                    success = false,
                    error = AuthError(
                        error = "parse_error",
                        errorDescription = "Failed to parse response: ${parseError.message}"
                    )
                )
            }
        }
    }
}

fun main() {
    val authManager = AuthenticationManager()
    
    // Success response
    val successResponse = """{"access_token": "token123", "expires_in": 3600}"""
    val result = authManager.handleAuthResponse(
        successResponse,
        AuthToken.serializer()
    )
    
    if (result.success) {
        println("Authentication successful: ${result.data}")
    } else {
        println("Authentication failed: ${result.error}")
    }
    
    // Error response
    val errorResponse = """
        {
            "error": "invalid_client",
            "error_description": "Client authentication failed"
        }
    """
    val errorResult = authManager.handleAuthResponse(
        errorResponse,
        AuthToken.serializer()
    )
    
    if (!errorResult.success) {
        println("Error: ${errorResult.error?.error}")
        println("Description: ${errorResult.error?.errorDescription}")
    }
}

Authentication Configuration Management

import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class OAuthConfig(
    val clientId: String,
    val clientSecret: String,
    val redirectUri: String,
    val scope: List<String>,
    val authorizationEndpoint: String,
    val tokenEndpoint: String,
    val userInfoEndpoint: String? = null,
    val pkceEnabled: Boolean = true
)

@Serializable
data class AuthenticationSettings(
    val providers: Map<String, OAuthConfig>,
    val defaultProvider: String,
    val sessionTimeout: Long,
    val tokenRefreshThreshold: Long,
    val securitySettings: SecuritySettings
)

@Serializable
data class SecuritySettings(
    val enableCsrfProtection: Boolean = true,
    val enableRateLimiting: Boolean = true,
    val maxLoginAttempts: Int = 5,
    val lockoutDuration: Long = 300000, // 5 minutes
    val requireHttps: Boolean = true
)

fun loadAuthConfiguration(configJson: String): AuthenticationSettings {
    val json = Json {
        ignoreUnknownKeys = true
        explicitNulls = false
        encodeDefaults = false
    }
    
    return json.decodeFromString<AuthenticationSettings>(configJson)
}

fun main() {
    val configJson = """
        {
            "providers": {
                "google": {
                    "client_id": "google_client_id",
                    "client_secret": "google_secret",
                    "redirect_uri": "https://app.example.com/auth/callback",
                    "scope": ["openid", "profile", "email"],
                    "authorization_endpoint": "https://accounts.google.com/o/oauth2/auth",
                    "token_endpoint": "https://oauth2.googleapis.com/token",
                    "user_info_endpoint": "https://www.googleapis.com/oauth2/v2/userinfo",
                    "pkce_enabled": true
                },
                "github": {
                    "client_id": "github_client_id",
                    "client_secret": "github_secret",
                    "redirect_uri": "https://app.example.com/auth/callback",
                    "scope": ["user:email"],
                    "authorization_endpoint": "https://github.com/login/oauth/authorize",
                    "token_endpoint": "https://github.com/login/oauth/access_token"
                }
            },
            "default_provider": "google",
            "session_timeout": 86400000,
            "token_refresh_threshold": 300000,
            "security_settings": {
                "enable_csrf_protection": true,
                "enable_rate_limiting": true,
                "max_login_attempts": 3,
                "lockout_duration": 600000,
                "require_https": true
            }
        }
    """
    
    val config = loadAuthConfiguration(configJson)
    println("Default Provider: ${config.defaultProvider}")
    println("Providers: ${config.providers.keys}")
    println("CSRF Protection: ${config.securitySettings.enableCsrfProtection}")
}