DPoP को अपनाने के बारे में गाइड

इस गाइड में, Google के OAuth प्लैटफ़ॉर्म के साथ OAuth 2.0 इंटिग्रेशन में डीपीओपी (पोज़ेशन का सबूत दिखाने की सुविधा) लागू करने का तरीका बताया गया है. RFC 9449 में बताई गई डीपीओपी की सुविधा, आपके ऐप्लिकेशन को टोकन की चोरी और रीप्ले हमलों से बचाती है. इसके लिए, यह टोकन को क्लाइंट की ओर से जनरेट किए गए एसिमेट्रिक कुंजी के जोड़े से क्रिप्टोग्राफ़िक तरीके से बाइंड करती है.

ऑथराइज़ेशन कोड फ़्लो में बदलाव

मौजूदा OAuth 2.0 ऑथराइज़ेशन कोड फ़्लो में डीपीओपी जोड़ने के लिए, कुंजी का जोड़ा जनरेट और सेव करना, डीपीओपी सबूत वाला JWT बनाना, और ऑथराइज़ेशन कोड को रीफ़्रेश टोकन से बदलने पर, सबूत को एचटीटीपी हेडर के तौर पर शामिल करना ज़रूरी है. यह तरीका, पहली इमेज के पांचवें और छठे चरण में दिखाया गया है.

डीपीओपी के साथ ऑथराइज़ेशन कोड फ़्लो
पहली इमेज. डीपीओपी का इस्तेमाल करके, ऑथराइज़ेशन कोड फ़्लो में होने वाले इवेंट का क्रम.

ऑथराइज़ेशन कोड का अनुरोध

ऑथराइज़ेशन का अनुरोध सामान्य तरीके से बनाया जाता है. उदाहरण के लिए:

$ 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 की पाबंदियां लागू होती हैं. एसपीए को सुरक्षित करने के लिए, ट्रैफ़िक को बैकएंड-फ़ॉर-फ़्रंटएंड (बीएफ़एफ़) के ज़रिए रूट करें. बीएफ़एफ़, कॉन्फ़िडेंशियल क्लाइंट के तौर पर काम करता है, access_type=offline की सुविधा चालू करता है, और रीफ़्रेश टोकन को सर्वर-साइड पर बाइंड करने के लिए डीपीओपी का इस्तेमाल करता है.

डीपीओपी सबूत बनाना

सबूत में, 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 निश्चित वैल्यू हैं. Google के टोकन एंडपॉइंट पर अनुरोध करने पर, इनमें कोई बदलाव नहीं होता.

अन्य दो दावे: iat और jti हर अनुरोध के लिए जनरेट किए जाने चाहिए. iat की वैल्यू, जारी किए जाने का टाइमस्टैंप होती है. यह हर अनुरोध के हिसाब से बदलती है. JWT आईडी (jti) दावे की वैल्यू, एक्सचेंज के टाइप पर निर्भर करती है. जब ऑथराइज़ेशन कोड को ऐक्सेस और रीफ़्रेश टोकन के लिए एक्सचेंज किया जाता है, तो jti की वैल्यू, ऑथराइज़ेशन कोड का Base-64 और यूआरएल-एनकोडेड SHA256 हैश होती है. जैसे, jti = BASE64URL(SHA-256(authorization_code)).

यहां पेलोड बॉडी का एक उदाहरण दिया गया है:

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

टोकन के अनुरोध में, DPoP एचटीटीपी हेडर में सीधे तौर पर इस्तेमाल करने के लिए, 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-Nonce एचटीटीपी हेडर के साथ मिलता है. उदाहरण के लिए:

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 के ऑथराइज़ेशन सर्वर से जनरेट किए गए नॉनस को, टोकन के हर अगले अनुरोध में शामिल करना ज़रूरी है. ध्यान दें कि नॉनस वैल्यू का इस्तेमाल सिर्फ़ एक बार किया जाता है. अगर नॉनस वैल्यू मौजूद नहीं है, अमान्य है, उसकी समयसीमा खत्म हो गई है या उसका फिर से इस्तेमाल किया गया है, तो उसे एचटीटीपी 400 रिस्पॉन्स के साथ अस्वीकार कर दिया जाता है. इस मामले में, फिर से कोशिश करने के लिए एक नया नॉनस मिलता है.

टोकन रीफ़्रेश फ़्लो में बदलाव

मौजूदा OAuth 2.0 टोकन रीफ़्रेश फ़्लो को अपडेट करने के लिए, रीफ़्रेश टोकन को नए टोकन के लिए एक्सचेंज करते समय, डीपीओपी सबूत को एचटीटीपी हेडर के तौर पर जनरेट और भेजा जाना चाहिए. यह तरीका, दूसरी इमेज के दूसरे से पांचवें चरण में दिखाया गया है.

डीपीओपी के साथ टोकन रीफ़्रेश करने का तरीका
दूसरी इमेज. गड़बड़ी को ठीक करने और फिर से कोशिश करने की सुविधा के साथ, टोकन रीफ़्रेश फ़्लो में होने वाले इवेंट का क्रम.

डीपीओपी सबूत बनाना

टोकन रीफ़्रेश के लिए सबूत बनाने का तरीका, ऑथराइज़ेशन कोड के मामले से अलग होता है. JOSE हेडर को उसी तरीके से बनाया जाता है जैसा कि ऑथराइज़ेशन कोड का अनुरोध बनाते समय बताया गया था. सबूत की बॉडी को भी इसी तरह बनाया जाता है. हालांकि, इसमें nonce दावा शामिल होता है और jti में एक यूनीक रैंडम स्ट्रिंग होती है.

पेलोड बॉडी बनाने के लिए, पहले मिले DPoP-Nonce एचटीटीपी हेडर की वैल्यू को nonce दावे में शामिल करना ज़रूरी है. साथ ही, हर अनुरोध के लिए, जारी किए जाने के टाइमस्टैंप (iat) को अपडेट करना ज़रूरी है. JWT आईडी (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
}

टोकन के अनुरोध में, डीपीओपी एचटीटीपी हेडर में सीधे तौर पर इस्तेमाल करने के लिए, 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"

जब समयसीमा खत्म हो चुके, गलत या फिर से इस्तेमाल किए गए नॉनस का इस्तेमाल किया जाता है या जब अलग-अलग OAuth वर्कफ़्लो के बीच ट्रांज़िशन किया जाता है (जैसे, शुरुआती ऑथराइज़ेशन कोड एक्सचेंज से टोकन रीफ़्रेश के अनुरोध पर जाना), तो Google का सर्वर, वर्कफ़्लो आइसोलेशन लागू करता है. इसका मतलब है कि सर्वर, नए वर्कफ़्लो के लिए एक नया नॉनस नेमस्पेस बनाने के लिए, एचटीटीपी 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 का इस्तेमाल करना और सबसे सही तरीके लेख पढ़ें.