OAuth 2.0 و JWT التفويض على Go: من النظرية إلى التطبيق
\n\nتطبيقات الويب الحديثة وواجهات API نادرًا ما تستغني عن نظام موثوق للمصادقة والتفويض. معياران — OAuth 2.0 و JWT (JSON Web Tokens) — أصبحا المعيارين الفعليين في عالم التطوير. في هذه المقالة سنشرح بأمثلة عملية كيفية تنفيذ تفويض OAuth 2.0 باستخدام JWT بلغة Go (Golang).
\n\nسنقوم بإنشاء خادم REST API بسيط يقوم بإصدار رموز JWT بعد المصادقة الناجحة عبر مزود OAuth 2.0 (مثل Google) والتحقق منها عند كل طلب.
\n\n1. أساسيات OAuth 2.0 و JWT
\n\nقبل كتابة الكود، دعنا نفهم المصطلحات بإيجاز.
\n\n- \n
- OAuth 2.0 — بروتوكول تفويض يسمح للمستخدمين بمنح أطراف ثالثة وصولًا محدودًا إلى مواردهم دون مشاركة اسم المستخدم وكلمة المرور. الأدوار الرئيسية: مالك المورد (المستخدم)، العميل (تطبيقنا)، خادم التفويض (الخادم الذي يصدر الرموز)، خادم الموارد (الخادم الذي يحتوي على البيانات المحمية). \n
- JWT — تنسيق رمز مضغوط وآمن لعناوين URL يتكون من ثلاثة أجزاء: header و payload و signature. يمكن أن يحتوي JWT على أي بيانات (claims) ويتم توقيعه بمفتاح سري أو زوج مفاتيح RSA. \n
في تنفيذنا، سيتم استخدام OAuth 2.0 للحصول على رمز الهوية من المزود (Google)، ثم سنقوم بإنشاء JWT خاص بنا للعمل اللاحق مع واجهة API الخاصة بنا.
\n\n2. إعداد المشروع والتبعيات
\n\nلنقم بإنشاء مشروع جديد على Go وتثبيت الحزم اللازمة:
\n\nmkdir go-oauth-jwt\ncd go-oauth-jwt\ngo mod init example.com/go-oauth-jwt\ngo get github.com/gin-gonic/gin\ngo get github.com/golang-jwt/jwt/v5\ngo get golang.org/x/oauth2\ngo get golang.org/x/oauth2/google\n\nسنستخدم:
\n- \n
- Gin — إطار عمل HTTP سريع لـ Go. \n
- golang-jwt — مكتبة للعمل مع JWT. \n
- oauth2 — مكتبة Go القياسية لـ OAuth 2.0. \n
ستحتاج أيضًا إلى تسجيل تطبيق في Google Cloud Console، وإنشاء OAuth 2.0 Client ID و Secret، وتحديد Redirect URI (مثل http://localhost:8080/auth/google/callback).
3. تنفيذ تسجيل الدخول عبر OAuth 2.0 باستخدام Google
\n\nلنبدأ بإنشاء تكوين OAuth 2.0 ونقاط النهاية لتسجيل الدخول والاستدعاء.
\n\npackage main\n\nimport (\n "crypto/rand"\n "encoding/base64"\n "net/http"\n "os"\n\n "github.com/gin-gonic/gin"\n "golang.org/x/oauth2"\n "golang.org/x/oauth2/google"\n)\n\nvar (\n googleOauthConfig *oauth2.Config\n oauthStateString string\n)\n\nfunc init() {\n googleOauthConfig = &oauth2.Config{\n RedirectURL: "http://localhost:8080/auth/google/callback",\n ClientID: os.Getenv("GOOGLE_CLIENT_ID"),\n ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"),\n Scopes: []string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"},\n Endpoint: google.Endpoint,\n }\n // ننشئ رمز state عشوائي للحماية من CSRF\n b := make([]byte, 32)\n rand.Read(b)\n oauthStateString = base64.StdEncoding.EncodeToString(b)\n}\n\nfunc main() {\n r := gin.Default()\n\n r.GET("/auth/google/login", handleGoogleLogin)\n r.GET("/auth/google/callback", handleGoogleCallback)\n\n r.Run(":8080")\n}\n\nfunc handleGoogleLogin(c *gin.Context) {\n url := googleOauthConfig.AuthCodeURL(oauthStateString)\n c.Redirect(http.StatusTemporaryRedirect, url)\n}\n\nfunc handleGoogleCallback(c *gin.Context) {\n state := c.Query("state")\n if state != oauthStateString {\n c.JSON(http.StatusBadRequest, gin.H{"error": "invalid oauth state"})\n return\n }\n\n code := c.Query("code")\n token, err := googl