jwt --- رموز JSON المميزة على الويب (JSON Web Tokens)

تنفيذ صغير لمعيار رمز JSON المميز على الويب (JWT) لأجل MicroPython. وJWT هو حاوية مدمجة وآمنة لاستخدامها في عناوين URL لحمولة JSON يوقّعها المُصدِر بحيث يستطيع المتحقّق كشف العبث وانتهاء الصلاحية.

هذا الإصدار المنقول إلى MicroPython صغير عن قصد:

  • يُدعَم HS256 فقط (HMAC مع SHA-256). أما خوارزميات RSA / ECDSA / EdDSA فغير منفّذة؛ ويجب أن يتشارك المُصدِر والمتحقّق مفتاحًا سريًا متماثلًا.

  • تُحترَم مطالبة exp (انتهاء الصلاحية) الاختيارية. أما المطالبات المسجّلة الأخرى (nbf، iat، iss، aud، sub، jti) فتُمرَّر دون تغيير في الحمولة لكنها لا تُتحقَّق؛ ويتحقّق منها التطبيق إن كان معنيًا بها.

  • لا توجد معالجة لتدوير المفاتيح، أو JWKS، أو قائمة الجمهور.

الدوال

jwt.encode(payload: dict, key: bytes | str, algorithm: str = 'HS256') str

ترميز payload في رمز JWT موقَّع وإرجاع التسلسل المدمج الناتج (سلسلة str من الشكل "header.payload.signature"، كل مقطع مُرمَّز بترميز Base64-URL).

payload

قاموس قابل للتسلسل بصيغة JSON من المطالبات. المطالبات القياسية (exp، iat، sub، ...) تُمرَّر حرفيًا؛ ويُسمح بمفاتيح التطبيق المخصّصة.

key

السر المشترك لـ HMAC. يُفضَّل bytes؛ أما str فيُرمَّز تلقائيًا بترميز UTF-8.

algorithm

يجب أن يكون "HS256" (القيمة الافتراضية). أي قيمة أخرى تطلق exceptions.InvalidAlgorithmError.

jwt.decode(token: str, key: bytes | str, algorithms: list = ['HS256']) dict

التحقّق من توقيع token و(إن وُجدت) مطالبة exp الخاصة به، وإرجاع قاموس الحمولة المفكوك ترميزه. تطلق فئة مشتقة من exceptions.PyJWTError عند أي فشل - وعادة ما يلتقط المستدعي الفئة الأساسية.

token

سلسلة JWT المدمجة التي تنتجها encode() (أو أي مُصدِر متوافق).

key

السر المشترك لـ HMAC. يجب أن يطابق المفتاح المستخدَم وقت الترميز.

algorithms

قائمة الخوارزميات التي يقبلها المستدعي. يجب أن تكون HS256 موجودة، ويجب أن يكون alg في ترويسة الرمز ضمن القائمة. القيمة الافتراضية هي ["HS256"].

تطلق:

  • exceptions.InvalidAlgorithmError -- HS256 غير موجودة في algorithms، أو أن alg في ترويسة الرمز أي شيء آخر غير HS256.

  • exceptions.InvalidTokenError -- الرمز مشوّه (لا ينقسم إلى ثلاثة مقاطع بترميز Base64-URL، أو فشل أحد المقاطع في فك الترميز / التحليل كـ JSON).

  • exceptions.InvalidSignatureError -- لا يطابق التوقيع قيمة HMAC المُعاد حسابها على الترويسة والحمولة.

  • exceptions.ExpiredSignatureError -- تحتوي الحمولة على مطالبة exp تقل قيمتها عن وقت Unix الحالي. يجب ضبط ساعة الكاميرا لكي يكون لهذا التحقّق معنى؛ انظر ntptime.settime().

الاستثناءات

تُدرَج جميع الاستثناءات تحت سمة الوحدة jwt.exceptions.

exception exceptions.PyJWTError

الفئة الأساسية لكل حالات فشل JWT. التقط هذه إذا لم تكن بحاجة إلى التمييز بين الحالات أدناه.

exception exceptions.InvalidTokenError

الرمز غير صالح بنيويًا -- عدد خاطئ من المقاطع، أو مقطع يفشل في فك ترميز Base64، أو JSON يفشل في التحليل. فئة مشتقة من exceptions.PyJWTError.

exception exceptions.InvalidAlgorithmError

خوارزمية الرمز غير موجودة في القائمة التي يقبلها المستدعي، أو أن المستدعي طلب خوارزمية غير HS256. فئة مشتقة من exceptions.PyJWTError.

exception exceptions.InvalidSignatureError

لا يطابق توقيع الرمز قيمة HMAC المحسوبة من الترويسة والحمولة باستخدام المفتاح المُورَّد. فئة مشتقة من exceptions.PyJWTError.

exception exceptions.ExpiredSignatureError

مطالبة exp الخاصة بالرمز في الماضي. فئة مشتقة من exceptions.PyJWTError. يجب ضبط وقت ساعة الحائط للكاميرا (عبر ntptime.settime()) لكي يكون لهذا التحقّق معنى.

مثال

دورة إصدار/تحقّق بسر متماثل:

import jwt
import time

SECRET = b"keep-this-out-of-source-control"

payload = {
    "sub": "kitchen-cam",
    "role": "admin",
    "exp": time.time() + 3600,                 # 1 hour from now
}

token = jwt.encode(payload, SECRET)
print("token:", token)

try:
    claims = jwt.decode(token, SECRET)
except jwt.exceptions.PyJWTError as e:
    print("rejected:", type(e).__name__)
else:
    print("subject:", claims["sub"])

يُستخدَم السر نفسه للتوقيع والتحقّق، لذا يجب أن يحمل المتحقّق المفتاح الذي استخدمه المُصدِر. أما الخدمات التي تكون فيها الكاميرا هي المُصدِر (التي توزّع الرموز على هاتف مقترن)، فاحتفظ بالسر على نظام ملفات الكاميرا وعامله كأي بيانات اعتماد أخرى طويلة الأمد -- انظر العمليات: المفاتيح والانتهاء واستكشاف الأخطاء وإصلاحها.

ملاحظة

تعتمد هذه الوحدة على hmac وhashlib، وكلتاهما مدمجتان في الكاميرا. ولا حاجة إلى أي إعداد خارجي. يمكن أن تكون المفاتيح بأي طول، لكن بنية HMAC تكون أكثر فائدة مع مفاتيح لا يقل طولها عن حجم كتلة SHA-256 (32 بايت).