ডিপিওপি দত্তক গ্রহণ নির্দেশিকা

এই নির্দেশিকায় গুগলের OAuth প্ল্যাটফর্মের সাথে আপনার OAuth 2.0 ইন্টিগ্রেশনে কীভাবে DPoP (Demonstrating Proof-of-Possession) প্রয়োগ করতে হয়, তার বিস্তারিত বর্ণনা দেওয়া হয়েছে। 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"

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

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

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

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

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

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

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

প্রুফ পেলোডটি তৈরি করতে চারটি মানের প্রয়োজন হয়।

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

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

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

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

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

$ curl -X POST https://oauth2.googleapis.com/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6\
       IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiVkM5MXk5WllkZlNXYUR2OEphSTZneDVpZ\
       k93MnJuM1lkcWtBQjUxVXU2RSIsInkiOiJpa1BqT3RlYTRrN2ZXUFZyUll3YUE0V3\
       c2aVZZM3BPT0lDb3RId3dHVjNvIn19.eyJqdGkiOiJvMjlDTjhMSVkwbF9OOGl5NS\
       1pbG9uMWd1YWQ5TkZRSEZPZFhUenJCTmNrIiwiaHRtIjoiUE9TVCIsImh0dSI6Imh\
       0dHBzOi8vb2F1dGgyLmdvb2dsZWFwaXMuY29tL3Rva2VuIiwiaWF0IjoxNzg0ODIy\
       MDI1fQ.OSdQCmqTng_uZmGK5UXf8hcEMtoOu7ucmYtl5mx4901RXnj6fJRJQmIeTq\
       fhprRBTG_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"
}

গুগলের অথরাইজেশন সার্ভার দ্বারা তৈরি ননসটি পরবর্তী প্রতিটি টোকেন অনুরোধে অবশ্যই অন্তর্ভুক্ত করতে হবে। মনে রাখবেন যে একটি ননস ভ্যালু শুধুমাত্র একবারই ব্যবহার করা হয় এবং একটি অনুপস্থিত, অবৈধ, মেয়াদোত্তীর্ণ বা পুনঃব্যবহৃত ননস ভ্যালু HTTP 400 রেসপন্স সহ প্রত্যাখ্যান করা হয়। এই ক্ষেত্রে, পুনরায় চেষ্টার জন্য ব্যবহারের উদ্দেশ্যে একটি নতুন ননস ফেরত দেওয়া হয়।

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

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

DPoP সহ টোকেন রিফ্রেশ ফ্লো
চিত্র ২. ত্রুটি পরিচালনা এবং পুনঃপ্রচেষ্টাসহ টোকেন রিফ্রেশ ফ্লো-এর ঘটনাক্রম।

ডিপিওপি প্রমাণ তৈরি করা

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

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

এটি jti , nonce , এবং iat ধারণকারী একটি উদাহরণ পেলোড বডি:

{
  "jti": "o29CN8ZIY0l_K8iy5-ilon1gwad9NF6HFOdXTzrBNck",
  "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: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6\
       IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiVkM5MXk5WllkZlNXYUR2OEphSTZneDVpZ\
       k93MnJuM1lkcWtBQjUxVXU2RSIsInkiOiJpa1BqT3RlYTRrN2ZXUFZyUll3YUE0V3\
       c2aVZZM3BPT0lDb3RId3dHVjNvIn19.eyJqdGkiOiJvMjlDTjhaSVkwbF9LOGl5NS\
       1pbG9uMWd1YWQ5TkY2SEZPZFhUenJCTmNrIiwiaHRtIjoiUE9TVCIsImh0dSI6Imh\
       0dHBzOi8vb2F1dGgyLmdvb2dsZWFwaXMuY29tL3Rva2VuIiwibm9uY2UiOiJBTjNY\
       d0pqWnNqbmIwWnVXa1JsZWs4UVU3d1ktWmhmLTVJUDZ0TzB0T1J6MEtndERUMUJvO\
       EZYLXc0bnozcjVsbmVwSSIsImlhdCI6MTc4NDgyMjAyNX0.MEQCIDm09AXo2c9sov\
       GrTUkrbEB_k9mra_Dkji-CQ9mSZVP1AiBxbiqkCE7Dt9RKyUT_3kj7q1vCvVggwnW\
       JNX3P3vO1mw" \
  --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"

যখন একটি মেয়াদোত্তীর্ণ, ভুল, বা পুনঃব্যবহৃত ননস (nonce) ব্যবহার করা হয় অথবা যখন বিভিন্ন OAuth ওয়ার্কফ্লোর মধ্যে পরিবর্তন করা হয় (যেমন প্রাথমিক অথরাইজেশন কোড এক্সচেঞ্জ থেকে টোকেন রিফ্রেশ অনুরোধে যাওয়ার সময়), তখন গুগলের সার্ভার ওয়ার্কফ্লো আইসোলেশন প্রয়োগ করে। এর অর্থ হলো, নতুন ওয়ার্কফ্লোর জন্য একটি নতুন ননস নেমস্পেস স্থাপন করতে সার্ভারটি একটি 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",
  "challenge": "AO4t07IXuovyCbtLEr6VVFZQ_Kb78MMOXTt6-CyZpsJeF62HZ3P_EW55XbWqYcU76Jg"
}

পরবর্তী অনুরোধে ব্যবহারের জন্য DPoP-Nonce মানটি সংরক্ষণ করুন।

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