DPoP অ্যাডপশন গাইড

Google-এর OAuth প্ল্যাটফর্মের সাথে আপনার OAuth 2.0 ইন্টিগ্রেশনে কীভাবে DPoP (মালিকানার প্রমাণ প্রদর্শন) প্রয়োগ করবেন তা এই গাইডে বিস্তারিতভাবে বলা হয়েছে। DPoP (RFC 9449-এ সংজ্ঞায়িত) আপনার অ্যাপ্লিকেশনকে টোকেন চুরি ও রিপ্লে অ্যাটাক থেকে সুরক্ষিত রাখে। এটি করার জন্য, ক্লায়েন্টের তৈরি করা অ্যাসিমেট্রিক কী পেয়ারের সাথে টোকেনকে ক্রিপ্টোগ্রাফিকভাবে বাইন্ড করা হয়।

অথরাইজেশন কোড ফ্লো সংক্রান্ত পরিবর্তন

আগে থেকে থাকা OAuth 2.0 অথরাইজেশন কোডের ফ্লোতে DPoP যোগ করতে হলে, একটি কীপেয়ার তৈরি ও স্টোর করতে হবে, DPoP প্রুফ JWT তৈরি করতে হবে এবং অথরাইজেশন কোড রিফ্রেশ টোকেনের সাথে এক্সচেঞ্জ করার সময় HTTP হেডার হিসেবে প্রুফ যোগ করতে হবে, যেমনটি চিত্র ১-এর ৫ ও ৬ নম্বর ধাপে দেখানো হয়েছে।

DPoP-এর সাথে অথরাইজেশন কোড ফ্লো
ছবি ১. DPoP ব্যবহার করে অথরাইজেশন কোড ফ্লোতে ইভেন্টের ক্রম।

অনুমোদন কোডের অনুরোধ

অনুমোদনের অনুরোধ সাধারণত তৈরি করা হয়। যেমন:

$ curl -G "https://accounts.google.com/o/oauth2/v2/auth" \
  --data-urlencode "client_id=YOUR_CLIENT_ID.apps.googleusercontent.com" \
  --data-urlencode "redirect_uri=http://127.0.0.1:8080" \
  --data-urlencode "response_type=code" \
  --data-urlencode "scope=calendar.readonly" \
  --data-urlencode "state=AI1Bvapj7E5SDmtW4gohcA" \
  --data-urlencode "code_challenge=PO4pPROl-31Wy9fVZ7uTW9Ga6CrjrSKsf4AAtx_JNM8" \
  --data-urlencode "code_challenge_method=S256" \
  --data-urlencode "nonce=PrMfmSNAvJFPQ7GnlEKUaw" \
  --data-urlencode "access_type=offline" \
  --data-urlencode "prompt=consent"

রিডাইরেক্ট URI প্যারামিটার হিসেবে ফেরত আসা অথরাইজেশন কোডটি DPoP প্রুফ তৈরি করতে ব্যবহার করা হয়। টোকেন এন্ডপয়েন্টে পরবর্তী সব অনুরোধে HTTP হেডার হিসেবে অন্তর্ভুক্ত প্রমাণের সাথে রিফ্রেশ টোকেনটি বাউন্ড করা থাকে।

এর প্রয়োজনীয়তা এবং DPoP-Nonce হেডারে CORS-এর সীমাবদ্ধতার কারণে, client_secret পিওর, সিক্রেটলেস ক্লায়েন্ট-সাইড SPA সরাসরি DPoP ব্যবহার করতে পারে না। SPA সুরক্ষিত করতে, ব্যাকএন্ড-ফর-ফ্রন্টএন্ড (BFF)-এর মাধ্যমে ট্রাফিক রুট করুন যা গোপনীয় ক্লায়েন্ট হিসেবে কাজ করে, access_type=offline চালু করে এবং সার্ভার-সাইডে রিফ্রেশ টোকেন বাইন্ড করতে DPoP ব্যবহার করে।

DPoP প্রমাণ তৈরি করুন

প্রমাণে একটি JOSE হেডার এবং একটি পেলোড থাকে।

হেডার তৈরি করতে, EC P-256 (ES256) কীপেয়ার তৈরি করুন এবং jwk প্যারামিটারে সর্বজনীন কী কোঅর্ডিনেট (x এবং y) অন্তর্ভুক্ত করুন। RSA কীপেয়ার (RS256) ব্যবহার করা যায়, তবে ES256 ব্যবহার করার সাজেশন দেওয়া হয় কারণ এটির সিগনেচারের সাইজ ছোট এবং কম্পিউটেশনাল খরচ কম।

এটি একটি JOSE হেডারের উদাহরণ:

{
  "typ": "dpop+jwt",
  "alg": "ES256",
  "jwk": {
    "kty": "EC",
    "crv": "P-256",
    "x": "VC91y9ZYdfSWaDv8JaI6gx5ifOw2rn3YdqkAB51Uu6E",
    "y": "ikPjOtea4k7fWPVrRYwaA4Ww6iVY3pOOICotHwwGV3o"
  }
}

প্রমাণ পেলোড তৈরি করতে, চারটি দাবি প্রয়োজন (htm, htu, iat, এবং jti), সাথে একটি ঐচ্ছিক nonce দাবি।

দুটি দাবি: htm: POST এবং htu: https://oauth2.googleapis.com/token হল নির্দিষ্ট মান এবং Google-এর টোকেন এন্ডপয়েন্টে অনুরোধ করার সময় পরিবর্তন হয় না।

অন্য দুটি দাবি: প্রতিটি অনুরোধের জন্য iat এবং jti অবশ্যই তৈরি করতে হবে। iat-এর ভ্যালু হল ইস্যু করার টাইমস্ট্যাম্প এবং প্রতিটি অনুরোধের জন্য এটি পরিবর্তিত হয়। JWT ID (jti) দাবির ভ্যালু এক্সচেঞ্জের ধরনের উপর নির্ভর করে। অ্যাক্সেস ও রিফ্রেশ টোকেনের জন্য অনুমোদন কোড এক্সচেঞ্জ করা হলে, jti-এর ভ্যালু হল অনুমোদন কোডের Base-64 ও Url-এনকোড করা SHA256 হ্যাশ, যেমন jti = BASE64URL(SHA-256(authorization_code))। প্রাথমিক অথরাইজেশন কোড এক্সচেঞ্জে nonce দাবি ঐচ্ছিক এবং আপনার ক্লায়েন্টের কাছে আগে থেকেই পূর্ববর্তী অনুরোধ থেকে একটি বৈধ DPoP-Nonce থাকলে তবেই প্রয়োজন।

এটি একটি উদাহরণ পেলোড বডি:

{
  "jti": "o29CN8LIY0l_N8iy5-ilon1guad9NFQHFOdXTzrBNck",
  "htm": "POST",
  "htu": "https://oauth2.googleapis.com/token",
  "iat": 1784822025
}

JOSE হেডার এবং পেলোড বডি এনকোড করা হয় এবং টোকেন অনুরোধে DPoP HTTP হেডারে সরাসরি ব্যবহারের জন্য JWT (RFC 7519) হিসাবে সাইন করা হয়। ES256-এর জন্য, JWS স্বাক্ষরকে অবশ্যই কাঁচা IEEE P1363 ফর্ম্যাট (R || S, P-256-এর জন্য ৬৪ বাইট) ব্যবহার করতে হবে:

$ curl -X POST https://oauth2.googleapis.com/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiVkM5MXk5WllkZlNXYUR2OEphSTZneDVpZk93MnJuM1lkcWtBQjUxVXU2RSIsInkiOiJpa1BqT3RlYTRrN2ZXUFZyUll3YUE0V3c2aVZZM3BPT0lDb3RId3dHVjNvIn19.eyJqdGkiOiJvMjlDTjhMSVkwbF9OOGl5NS1pbG9uMWd1YWQ5TkZRSEZPZFhUenJCTmNrIiwiaHRtIjoiUE9TVCIsImh0dSI6Imh0dHBzOi8vb2F1dGgyLmdvb2dsZWFwaXMuY29tL3Rva2VuIiwiaWF0IjoxNzg0ODIyMDI1fQ.OSdQCmqTng_uZmGK5UXf8hcEMtoOu7ucmYtl5mx4901RXnj6fJRJQmIeTqfhprRBTG_RSJv2fPcWDqvQbDW7YA" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "code=4/0AXEQxIDNpLD-qpSIvjHb2Hl10uS_2sk2GBRpO8UJQ78YZF3hZ9LB9kTA1xYLD4xisi4C5w" \
  --data-urlencode "redirect_uri=http://127.0.0.1:8080" \
  --data-urlencode "client_id=YOUR_CLIENT_ID.apps.googleusercontent.com" \
  --data-urlencode "client_secret=YOUR_CLIENT_SECRET" \
  --data-urlencode "code_verifier=q8ZztyVv7HH8E2M-SEL8WaB-7CPs68rejN5UZ9OdYgo"

DPoP বাউন্ড রিফ্রেশ টোকেন DPoP-Nonce HTTP হেডারের সাথে ফেরত পাঠানো হয়, যেমন:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
DPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI

{
  "access_token": "ya29.a0ARGnu0aebRL97B91dmvm14gTug5wpItFf9MVWq12Hja6yv09A_qxa4T73_z2gFbf32qR4RXispQ7vnOzv6gn0APLQrF51LVa6AOqCVPH2Tupocv8y0JHu4ByEbvgXEEhiHEU8Xa9_w3i-PKBPsKWiLi210RCZdqJjLXkcRrGnoPPjbGPzOPtm6KCJjPrNHG16caOWecaCgYKASESARASFQHGX2MiBn7ihbbk_n-buCbOfl2TDA0206",
  "expires_in": 3599,
  "refresh_token": "1//06dUPZ9FIBQm3CgYIARAAGAYSNwF-L9IrJwuIEKUA_zbBPU-xoCDGM0QrDu7-jv7cMQZ0kARPUK9WhwfFFfbOVEgXDQKmFh4w9GM",
  "scope": "https://www.googleapis.com/auth/calendar.readonly",
  "token_type": "Bearer"
}

Google-এর অথরাইজেশন সার্ভার থেকে পাওয়া সাম্প্রতিকতম DPoP-Nonce সবসময় ক্যাশে করে পরবর্তী টোকেন অনুরোধে পাঠায়। ননস নির্দিষ্ট সময় অন্তর মেয়াদ শেষ হয়ে যায়, এবং কোনও প্রমাণে প্রয়োজনীয় nonce দাবি না থাকলে অথবা ভুল বা মেয়াদ শেষ হয়ে যাওয়া ননস থাকলে, সার্ভার HTTP 400 (use_dpop_nonce) রেসপন্স ফেরত পাঠায়, যাতে আবার চেষ্টা করার জন্য নতুন DPoP-Nonce হেডার থাকে।

টোকেন রিফ্রেশ ফ্লোতে পরিবর্তন

আগে থেকে থাকা OAuth 2.0 টোকেন রিফ্রেশ ফ্লো আপডেট করতে হলে, নতুন টোকেনের জন্য রিফ্রেশ টোকেন এক্সচেঞ্জ করার সময় HTTP হেডার হিসেবে DPoP প্রমাণ তৈরি করে পাঠাতে হবে। এটি ছবি ২-এর ২-৫ নম্বর ধাপে দেখানো হয়েছে।

DPoP-এর মাধ্যমে টোকেন রিফ্রেশ ফ্লো
ছবি ২. সমস্যা সমাধান ও আবার চেষ্টা করার সুবিধা সহ টোকেন রিফ্রেশ ফ্লোতে ইভেন্টের ক্রম।

DPoP প্রমাণ তৈরি করা হচ্ছে

টোকেন রিফ্রেশ করার জন্য প্রমাণ তৈরি করার পদ্ধতি অনুমোদন কোড সিনারিও থেকে আলাদা। JOSE হেডারটি আগে যেভাবে বর্ণনা করা হয়েছে সেই একইভাবে তৈরি করা হয়েছে যখন একটি অনুমোদন কোড অনুরোধ তৈরি করা হয়। প্রমাণ বডি একইভাবে তৈরি করা হয়, তবে এতে nonce দাবি এবং jti-এ একটি অনন্য র‍্যান্ডম স্ট্রিং থাকে।

পেলোড বডি তৈরি করতে, আগে ফেরত দেওয়া DPoP-Nonce HTTP হেডার ভ্যালু অবশ্যই nonce দাবিতে অন্তর্ভুক্ত করতে হবে এবং প্রতিটি অনুরোধের জন্য ইস্যু করার টাইমস্ট্যাম্প (iat) আপডেট করতে হবে। JWT আইডি (jti) হল একটি অনন্য র‍্যান্ডম স্ট্রিং যা বিল্ট-ইন WebCrypto API crypto.getRandomValues(new Uint8Array(24)) এবং Base64URL-এনকোডিং স্ট্রিং ব্যবহার করে প্রতিটি অনুরোধের জন্য তৈরি করা হয়।

এটি একটি উদাহরণ পেলোড বডি যাতে jti, nonce এবং iat রয়েছে:

{
  "jti": "o29CN8ZIY0l_K8iy5-ilon1guad9NF6HFOdXTzrBNck",
  "htm": "POST",
  "htu": "https://oauth2.googleapis.com/token",
  "nonce": "AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI",
  "iat": 1784822025
}

টোকেন অনুরোধে DPoP HTTP হেডারে সরাসরি ব্যবহারের জন্য JOSE হেডার এবং পেলোড বডি JWT (RFC7519) হিসেবে এনকোড করা হয়।

টোকেন রিফ্রেশ করার অনুরোধে প্রমাণটি DPoP হেডার হিসেবে যোগ করা হয়:

$ curl -X POST https://oauth2.googleapis.com/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiVkM5MXk5WllkZlNXYUR2OEphSTZneDVpZk93MnJuM1lkcWtBQjUxVXU2RSIsInkiOiJpa1BqT3RlYTRrN2ZXUFZyUll3YUE0V3c2aVZZM3BPT0lDb3RId3dHVjNvIn19.eyJqdGkiOiJvMjlDTjhaSVkwbF9LOGl5NS1pbG9uMWd1YWQ5TkY2SEZPZFhUenJCTmNrIiwiaHRtIjoiUE9TVCIsImh0dSI6Imh0dHBzOi8vb2F1dGgyLmdvb2dsZWFwaXMuY29tL3Rva2VuIiwibm9uY2UiOiJBTjNYd0pqWnNqbmIwWnVXa1JsZWs4UVU3d1ktWmhmLTVJUDZ0TzB0T1J6MEtndERUMUJvOEZYLXc0bnozcjVsbmVwSSIsImlhdCI6MTc4NDgyMjAyNX0.ObT0BejZz2yi8atNSStsQH-T2atr8OSOL4JD2ZJlU_VxbiqkCE7Dt9RKyUT_3kj7q1vCvVggwnWJNX3P3vO1mw" \
  --data-urlencode "grant_type=refresh_token" \
  --data-urlencode "refresh_token=1//06dUPZ9FIBQm3CgYIARAAGAYSNwF-L9IrJwuIEKUA_zbBPU-xoCDGM0QrDu7-jv7cMQZ0kARPUK9WhwfFFfbOVEgXDQKmFh4w9GM" \
  --data-urlencode "client_id=YOUR_CLIENT_ID.apps.googleusercontent.com" \
  --data-urlencode "client_secret=YOUR_CLIENT_SECRET"

মেয়াদ শেষ হয়ে যাওয়া বা ভুল ননস ব্যবহার করা হলে অথবা বিভিন্ন OAuth ওয়ার্কফ্লোর মধ্যে ট্রানজিশন করার সময় (যেমন, প্রাথমিক অথরাইজেশন কোড এক্সচেঞ্জ থেকে টোকেন রিফ্রেশ করার অনুরোধে পরিবর্তন করা) Google-এর সার্ভার ওয়ার্কফ্লো আইসোলেশন এনফোর্স করে। এর অর্থ হল, নতুন ওয়ার্কফ্লোর জন্য ফ্রেশ ননস নেমস্পেস প্রতিষ্ঠা করতে, সার্ভার কোনও শর্ত ছাড়াই HTTP 400 use_dpop_nonce চ্যালেঞ্জ সহ ননস প্রত্যাখ্যান করে।

এটি একটি উদাহরণ 400 প্রতিক্রিয়া যার জন্য একটি পুনঃপ্রচেষ্টা এবং একটি নতুন প্রমাণ প্রয়োজন DPoP-Nonce মান ব্যবহার করে তৈরি করা হবে:

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
DPoP-Nonce: AO4t07Kf85RJXmltUhiAiELLPPrJ4zOi66zWxU1uDZbhRcahFBYvT0WlcjSSXULXknSA

{
  "error": "use_dpop_nonce",
  "error_description": "New DPoP nonce issued due to invalid or expired challenge."
}

সফল হলে, একটি নতুন ননস এবং স্বল্প-স্থায়ী অ্যাক্সেস টোকেন ফেরত দেওয়া হয়:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
DPoP-Nonce: AO4t07IXuovyCbtLEr6VVFZQ_Kb78MMOXTt6-CyZpsJeF62HZ3P_EW55XbWqYcU76Jg=

{
  "access_token": "ya29.a0ARGnu0bDj9BAQYVbF5hi3vw-brBUZBZu1bnInk1hS7gueqEb6QPqUjDGb0MMj9A0QX5FRrJo3FDw-DEDtvVbRUdeCgjwsL_LVVFXz-p-MUyiFyRoufI4KC0Go9aq5cEjD_BWvOJLMSIY6_EnwnhqDgk0XxvzaaAxDnv8PXJAGev_UotcfApstqi0NCxbfi-6Kgull9QaCgYKAUQSARASFQHGX2MiZpMjRS6z4S0RjOkNxn2o1Q0206",
  "expires_in": 3599,
  "scope": "https://www.googleapis.com/auth/calendar.readonly",
  "token_type": "Bearer"
}

পরবর্তী অনুরোধে ব্যবহার করার জন্য DPoP-Nonce ভ্যালু সেভ করুন।

আরও বিবরণ ও সাজেশনের জন্য ওয়েব সার্ভার অ্যাপ্লিকেশনের জন্য OAuth 2.0 ব্যবহার করা এবং পেশাদার পদ্ধতি দেখুন।