عميل OAuth لـ Kotlin: التكامل مع المزودين عبر ktor-client و kotlinx-serialization
\n\nOAuth 2.0 هو بروتوكول تفويض قياسي يسمح للتطبيقات بالحصول على وصول محدود إلى موارد المستخدم دون نقل بيانات اعتماده. في نظام Kotlin البيئي، بفضل التوافق مع JVM والتعدد المنصات، يمكن تنفيذ عميل OAuth بكفاءة باستخدام المكتبات الحديثة. في هذه المقالة، سننظر في إنشاء عميل OAuth بلغة Kotlin باستخدام ktor-client لطلبات HTTP و kotlinx-serialization لمعالجة JSON.
المكونات الأساسية لعميل OAuth
\n- \n
- عميل HTTP —
ktor-clientلإرسال الطلبات إلى خوادم التفويض والموارد. \n - التسلسل —
kotlinx-serializationلتحليل الردود إلى فئات البيانات. \n - مخزن الرموز — في الذاكرة أو دائم (SharedPreferences، ملفات). \n
- مدير الحالات — معالجة authorization code، refresh token، حماية CSRF. \n
ربط التبعيات
\nللعمل مع OAuth في Kotlin، أضف التبعيات التالية إلى build.gradle.kts (أو build.gradle):
// build.gradle.kts\ndependencies {\n implementation("io.ktor:ktor-client-core:2.3.12")\n implementation("io.ktor:ktor-client-cio:2.3.12") // المحرك\n implementation("io.ktor:ktor-client-content-negotiation:2.3.12")\n implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.12")\n implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")\n implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")\n}\n\nالهندسة المعمارية الأساسية لعميل OAuth
\nسنقسم المنطق إلى ثلاث طبقات:
\n- \n
- النماذج (DTO) — فئات بيانات للطلبات/الردود. \n
- الخدمة — فئة تدير تدفقات التفويض. \n
- العميل — غلاف حول ktor-client مع طرق لمنح مختلفة. \n
نماذج البيانات
\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\n\n@Serializable\ndata class TokenResponse(\n @SerialName("access_token") val accessToken: String,\n @SerialName("token_type") val tokenType: String = "Bearer",\n @SerialName("expires_in") val expiresIn: Long? = null,\n @SerialName("refresh_token") val refreshToken: String? = null,\n @SerialName("scope") val scope: String? = null,\n @SerialName("id_token") val idToken: String? = null // لـ OpenID Connect\n)\n\n@Serializable\ndata class OAuthError(\n val error: String,\n @SerialName("error_description") val errorDescription: String? = null\n)\n\nعميل OAuth2
\nimport io.ktor.client.*\nimport io.ktor.client.call.*\nimport io.ktor.client.plugins.contentnegotiation.*\nimport io.ktor.client.request.*\nimport io.ktor.client.statement.*\nimport io.ktor.http.*\nimport io.ktor.serialization.kotlinx.json.*\nimport kotlinx.serialization.json.Json\n\nclass OAuth2Client(\n private val clientId: String,\n private val clientSecret: String,\n private val redirectUri: String,\n private val tokenEndpoint: String,\n private val authEndpoint: String\n) {\n private val httpClient = HttpClient(CIO) {\n install(ContentNegotiation) {\n json(Json {\n ignoreUnknownKeys = true\n isLenient = true\n })\n }\n }\n\n // توليد URL لإعادة التوجيه إلى صفحة التفويض\n fun getAuthorizationUrl(state: String, scope: String = "openid profile email"): String {\n return "$authEndpoint?response_type=code" +\n "&client_id=$clientId" +\n "&redirect_uri=${java.net.URLEncoder.encode(redirectUri, "UTF-8")}" +\n "&scope=${java.net.URLEncoder.encode(scope, "UTF-8")}