kotlinx-serialization-json
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
- kotlinx.serialization Official Page
- kotlinx.serialization Official Documentation
- JSON Processing Guide
- API Reference
- GitHub Repository
- Kotlin Official Site
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}")
}