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

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

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

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

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

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

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

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

DPoP सबूत बनाना

सबूत में एक JOSE हेडर और एक पेलोड होता है.

हेडर बनाने के लिए, EC P-256 (ES256) कीपेयर जनरेट करें. साथ ही, jwk पैरामीटर में सार्वजनिक पासकोड के कोऑर्डिनेट (x और y) शामिल करें. आरएसए कीपेयर (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 आईडी (jti) क्लेम की वैल्यू, एक्सचेंज के टाइप पर निर्भर करती है. जब किसी ऑथराइज़ेशन कोड को ऐक्सेस और रीफ़्रेश टोकन से बदला जाता है, तो jti की वैल्यू, ऑथराइज़ेशन कोड का Base-64 और यूआरएल कोड में बदला गया SHA256 हैश होती है. जैसे, jti = BASE64URL(SHA-256(authorization_code)). शुरुआती ऑथराइज़ेशन कोड एक्सचेंज पर nonce दावा करना ज़रूरी नहीं है. इसकी ज़रूरत सिर्फ़ तब पड़ती है, जब आपके क्लाइंट के पास पहले से ही पिछले अनुरोध का मान्य DPoP-Nonce मौजूद हो.

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

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

जेओएसई हेडर और पेलोड बॉडी को JWT (आरएफ़सी 7519) के तौर पर एन्कोड और साइन किया जाता है. इससे टोकन के अनुरोध में, DPoP एचटीटीपी हेडर में इसका सीधे तौर पर इस्तेमाल किया जा सकता है. ES256 के लिए, जेडब्ल्यूएस सिग्नेचर में रॉ IEEE P1363 फ़ॉर्मैट (R || S, P-256 के लिए 64 बाइट) का इस्तेमाल करना ज़रूरी है:

$ 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-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 के अनुमति देने वाले सर्वर से मिले सबसे नए DPoP-Nonce को हमेशा कैश मेमोरी में सेव करें और भेजें. नॉन्स समय-समय पर खत्म होते रहते हैं. अगर किसी सबूत में ज़रूरी nonce दावा मौजूद नहीं है या उसमें अमान्य या समयसीमा खत्म हो चुका नॉन्स शामिल है, तो सर्वर एचटीटीपी 400 (use_dpop_nonce) रिस्पॉन्स दिखाता है. इसमें एक नया DPoP-Nonce हेडर होता है, ताकि फिर से कोशिश की जा सके.

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

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

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

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

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

पेलोड बॉडी बनाने के लिए, पहले से मिले DPoP-Nonce एचटीटीपी हेडर की वैल्यू को 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
}

जेओएसई हेडर और पेलोड बॉडी को 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 का सर्वर वर्कफ़्लो को अलग-अलग रखता है. इसका मतलब है कि सर्वर, एचटीटीपी 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 का इस्तेमाल करना और सबसे सही तरीके देखें.