---
url: 'https://www.corbado.com/hi/blog/verifiable-credential-issuer-kaise-banaye'
title: 'डिजिटल क्रेडेंशियल जारीकर्ता कैसे बनाएं (डेवलपर्स गाइड)'
description: 'OpenID4VCI प्रोटोकॉल का उपयोग करके W3C वेरिफ़ाएबल क्रेडेंशियल जारीकर्ता बनाना सीखें। यह स्टेप-बाय-स्टेप गाइड आपको दिखाता है कि एक Next.js एप्लिकेशन कैसे बनाया जाए जो क्रिप्टोग्राफ़िक रूप से हस्ताक्षरित क्रेडेंशियल जारी करता है जो डिजिटल वॉलेट के साथ संगत '
lang: 'hi'
author: 'Amine'
date: '2025-08-20T15:39:16.541Z'
lastModified: '2026-03-25T10:07:36.540Z'
keywords: 'डिजिटल क्रेडेंशियल जारीकर्ता, ट्यूटोरियल जारीकर्ता, जारीकर्ता बनाएं'
category: 'Digital Credentials'
---

# डिजिटल क्रेडेंशियल जारीकर्ता कैसे बनाएं (डेवलपर्स गाइड)

## 1. परिचय

डिजिटल क्रेडेंशियल एक सुरक्षित और गोपनीयता-संरक्षण तरीके से पहचान और दावों को साबित करने
का एक शक्तिशाली तरीका है। लेकिन यूजर्स को ये क्रेडेंशियल पहली बार में कैसे मिलते हैं? यहीं
पर **Issuer** की भूमिका महत्वपूर्ण हो जाती है। एक [Issuer](https://www.corbado.com/glossary/issuer) एक विश्वसनीय
इकाई है - जैसे कि एक [सरकारी](https://www.corbado.com/passkeys-for-public-sector) एजेंसी, एक विश्वविद्यालय, या एक
बैंक - जो यूजर्स को डिजिटल रूप से हस्ताक्षरित क्रेडेंशियल बनाने और वितरित करने के लिए
जिम्मेदार है।

यह गाइड डिजिटल क्रेडेंशियल [Issuer](https://www.corbado.com/glossary/issuer) बनाने के लिए एक व्यापक,
स्टेप-बाय-स्टेप ट्यूटोरियल प्रदान करता है। हम **OpenID for Verifiable Credential Issuance
(OpenID4VCI)** प्रोटोकॉल पर ध्यान केंद्रित करेंगे, जो एक आधुनिक मानक है जो यह परिभाषित
करता है कि यूजर्स किसी [Issuer](https://www.corbado.com/glossary/issuer) से क्रेडेंशियल कैसे प्राप्त कर सकते हैं
और उन्हें अपने डिजिटल वॉलेट में सुरक्षित रूप से संग्रहीत कर सकते हैं।

अंतिम परिणाम एक कार्यात्मक [Next.js](https://www.corbado.com/blog/nextjs-passkeys) एप्लिकेशन होगा जो यह कर सकता
है:

1. एक सरल वेब फ़ॉर्म के माध्यम से यूजर डेटा स्वीकार करना।
2. एक सुरक्षित, वन-टाइम क्रेडेंशियल ऑफ़र जेनरेट करना।
3. ऑफ़र को QR कोड के रूप में प्रदर्शित करना ताकि यूजर अपने मोबाइल वॉलेट से स्कैन कर सके।
4. एक क्रिप्टोग्राफ़िक रूप से हस्ताक्षरित क्रेडेंशियल जारी करना जिसे यूजर सत्यापन के लिए
   संग्रहीत और प्रस्तुत कर सकता है।

### 1.1 शब्दावली को समझना: डिजिटल क्रेडेंशियल बनाम वेरिफ़ाएबल क्रेडेंशियल

आगे बढ़ने से पहले, दो संबंधित लेकिन अलग-अलग अवधारणाओं के बीच के अंतर को स्पष्ट करना
महत्वपूर्ण है:

- **डिजिटल क्रेडेंशियल (सामान्य शब्द):** यह एक व्यापक श्रेणी है जिसमें क्रेडेंशियल, प्रमाण
  पत्र, या अटेस्टेशन का कोई भी डिजिटल रूप शामिल है। इनमें सरल डिजिटल प्रमाण पत्र, बुनियादी
  डिजिटल बैज, या कोई भी इलेक्ट्रॉनिक रूप से संग्रहीत क्रेडेंशियल शामिल हो सकता है जिसमें
  क्रिप्टोग्राफ़िक सुरक्षा सुविधाएँ हो भी सकती हैं और नहीं भी।

- **वेरिफ़ाएबल क्रेडेंशियल (VCs - W3C स्टैंडर्ड):** यह एक विशिष्ट प्रकार का डिजिटल
  क्रेडेंशियल है जो W3C वेरिफ़ाएबल क्रेडेंशियल डेटा मॉडल मानक का पालन करता है। वेरिफ़ाएबल
  क्रेडेंशियल क्रिप्टोग्राफ़िक रूप से हस्ताक्षरित, टैम्पर-एविडेंट और गोपनीयता-सम्मान करने
  वाले क्रेडेंशियल होते हैं जिन्हें स्वतंत्र रूप से सत्यापित किया जा सकता है। उनमें
  विशिष्ट तकनीकी आवश्यकताएं शामिल हैं जैसे:
    - प्रामाणिकता और अखंडता के लिए क्रिप्टोग्राफ़िक हस्ताक्षर
    - मानकीकृत डेटा मॉडल और प्रारूप
    - गोपनीयता-संरक्षण प्रस्तुति तंत्र
    - इंटरऑपरेबल सत्यापन प्रोटोकॉल

**इस गाइड में, हम विशेष रूप से एक वेरिफ़ाएबल क्रेडेंशियल जारीकर्ता का निर्माण कर रहे हैं**
जो W3C मानक का पालन करता है, न कि केवल किसी डिजिटल क्रेडेंशियल सिस्टम का। हम जिस
[OpenID4VCI](https://www.corbado.com/glossary/openid4vci) प्रोटोकॉल का उपयोग कर रहे हैं, वह विशेष रूप से
वेरिफ़ाएबल क्रेडेंशियल जारी करने के लिए डिज़ाइन किया गया है, और हम जो JWT-VC प्रारूप लागू
करेंगे, वह वेरिफ़ाएबल क्रेडेंशियल के लिए W3C-अनुपालन प्रारूप है।

### 1.2 यह कैसे काम करता है

डिजिटल क्रेडेंशियल के पीछे का जादू एक सरल लेकिन शक्तिशाली "**ट्रस्ट ट्रायंगल**" मॉडल में
निहित है जिसमें तीन प्रमुख खिलाड़ी शामिल हैं:

- **Issuer:** एक विश्वसनीय प्राधिकरण (जैसे, एक [सरकारी](https://www.corbado.com/passkeys-for-public-sector)
  एजेंसी, विश्वविद्यालय, या बैंक) जो क्रिप्टोग्राफ़िक रूप से एक क्रेडेंशियल पर हस्ताक्षर
  करता है और उसे यूजर को जारी करता है। **यह वह भूमिका है जिसे हम इस गाइड में बना रहे
  हैं।**
- **Holder:** यूजर, जो क्रेडेंशियल प्राप्त करता है और उसे अपने डिवाइस पर एक व्यक्तिगत
  डिजिटल वॉलेट में सुरक्षित रूप से संग्रहीत करता है।
- **Verifier:** एक एप्लिकेशन या सेवा जिसे यूजर के क्रेडेंशियल की जांच करने की आवश्यकता
  होती है।

![W3C Verifiable Credentials Ecosystem](https://www.w3.org/TR/vc-data-model/diagrams/ecosystem.svg)

जारी करने का प्रवाह इस इकोसिस्टम में पहला कदम है। Issuer यूजर की जानकारी को मान्य करता है
और उन्हें एक क्रेडेंशियल प्रदान करता है। एक बार जब Holder के पास यह क्रेडेंशियल उनके वॉलेट
में होता है, तो वे अपनी पहचान या दावों को साबित करने के लिए इसे एक Verifier को प्रस्तुत कर
सकते हैं, जिससे ट्रायंगल पूरा होता है।

यहाँ अंतिम एप्लिकेशन का एक त्वरित अवलोकन है:

**चरण 1: यूजर डेटा इनपुट** यूजर एक नया क्रेडेंशियल अनुरोध करने के लिए अपनी व्यक्तिगत
जानकारी के साथ एक फ़ॉर्म भरता है।
![User Data Input Form](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/issuer_step_1_0733a9e1da.png)

**चरण 2: क्रेडेंशियल ऑफ़र जेनरेशन** एप्लिकेशन एक सुरक्षित क्रेडेंशियल ऑफ़र जेनरेट करता है,
जो एक QR कोड और एक पूर्व-अधिकृत कोड के रूप में प्रदर्शित होता है।
![Credential Offer QR Code](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/issuer_step_2_3f1881c473.png)

**चरण 3: वॉलेट इंटरैक्शन** यूजर संगत वॉलेट (जैसे, Sphereon
[Wallet](https://www.corbado.com/blog/digital-wallet-assurance)) के साथ QR कोड को स्कैन करता है और जारी करने को
अधिकृत करने के लिए एक पिन दर्ज करता है।
![Credential Offer on wallet](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/issuer_step_3_b80d689dfe.png)
![PIN Code insertion](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/issuer_step_4_ca8bad8d11.png)

**चरण 4: क्रेडेंशियल जारी किया गया** वॉलेट नए जारी किए गए डिजिटल क्रेडेंशियल को प्राप्त और
संग्रहीत करता है, जो भविष्य के उपयोग के लिए तैयार है।
![Confirming the credential details](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/issuer_step_5_55b8150597.png)
![Credential added](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/issuer_step_6_7f5ac5745d.png)

## 2. Issuer बनाने के लिए आवश्यक शर्तें

कोड में गोता लगाने से पहले, आइए उन मूलभूत ज्ञान और उपकरणों को कवर करें जिनकी आपको आवश्यकता
होगी। यह गाइड मानता है कि आपको वेब विकास अवधारणाओं की बुनियादी जानकारी है, लेकिन एक
क्रेडेंशियल जारीकर्ता बनाने के लिए निम्नलिखित आवश्यक शर्तें हैं।

### 2.1 प्रोटोकॉल विकल्प

हमारा Issuer खुले मानकों के एक सेट पर बनाया गया है जो वॉलेट और जारी करने वाली सेवाओं के
बीच इंटरऑपरेबिलिटी सुनिश्चित करता है। इस ट्यूटोरियल के लिए, हम निम्नलिखित पर ध्यान
केंद्रित करेंगे:

| मानक / प्रोटोकॉल                                                  | विवरण                                                                                                                                                                                                                                      |
| :---------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **OpenID4VCI**                                                    | **वेरिफ़ाएबल क्रेडेंशियल जारी करने के लिए OpenID।** यह मुख्य प्रोटोकॉल है जिसका हम उपयोग करेंगे। यह एक मानक प्रवाह को परिभाषित करता है कि कैसे एक यूजर (अपने वॉलेट के माध्यम से) एक Issuer से क्रेडेंशियल का अनुरोध और प्राप्त कर सकता है। |
| **[JWT-VC](https://www.w3.org/TR/vc-data-model/#json-web-token)** | **JWT-आधारित वेरिफ़ाएबल क्रेडेंशियल।** हम जो क्रेडेंशियल जारी करेंगे उसका प्रारूप। यह एक W3C मानक है जो वेरिफ़ाएबल क्रेडेंशियल को JSON वेब टोकन (JWTs) के रूप में एन्कोड करता है, जिससे वे कॉम्पैक्ट और वेब-अनुकूल बनते हैं।               |
| **[ISO mDoc](https://www.iso.org/standard/69084.html)**           | **ISO/IEC 18013-5।** मोबाइल ड्राइवर लाइसेंस (mDLs) के लिए अंतर्राष्ट्रीय मानक। जबकि हम एक JWT-VC जारी करते हैं, इसके भीतर के _claims_ mDoc डेटा मॉडल (जैसे, `eu.europa.ec.eudi.pid.1`) के साथ संगत होने के लिए संरचित हैं।                 |
| **OAuth 2.0**                                                     | OpenID4VCI द्वारा उपयोग किया जाने वाला अंतर्निहित प्राधिकरण ढांचा। हम एक `pre-authorized_code` प्रवाह लागू करेंगे, जो सुरक्षित और उपयोगकर्ता-अनुकूल क्रेडेंशियल जारी करने के लिए डिज़ाइन किया गया एक विशिष्ट अनुदान प्रकार है।             |

#### 2.1.1 प्राधिकरण प्रवाह: पूर्व-अधिकृत बनाम प्राधिकरण कोड

[OpenID4VCI](https://www.corbado.com/glossary/openid4vci) क्रेडेंशियल जारी करने के लिए दो प्राथमिक प्राधिकरण
प्रवाह का समर्थन करता है:

1. **प्री-ऑथराइज्ड कोड फ्लो:** इस फ्लो में, Issuer एक अल्पकालिक, एकल-उपयोग कोड
   (`pre-authorized_code`) उत्पन्न करता है जो यूजर के लिए तुरंत उपलब्ध होता है। यूजर का
   वॉलेट तब इस कोड को सीधे क्रेडेंशियल के लिए एक्सचेंज कर सकता है। यह फ्लो उन परिदृश्यों
   के लिए आदर्श है जहां यूजर पहले से ही Issuer की वेबसाइट पर प्रमाणित और उपस्थित है,
   क्योंकि यह रीडायरेक्ट के बिना एक सहज, तत्काल जारी करने का अनुभव प्रदान करता है।

2. **ऑथराइजेशन कोड फ्लो:** यह मानक [OAuth 2.0](https://www.corbado.com/glossary/oauth2) फ्लो है, जहां यूजर को
   सहमति देने के लिए एक प्राधिकरण सर्वर पर रीडायरेक्ट किया जाता है। अनुमोदन के बाद, सर्वर
   एक `authorization_code` को एक पंजीकृत `redirect_uri` पर वापस भेजता है। यह फ्लो तीसरे
   पक्ष के एप्लिकेशन के लिए अधिक उपयुक्त है जो यूजर की ओर से जारी करने की प्रक्रिया शुरू
   करते हैं।

**इस ट्यूटोरियल के लिए, हम `pre-authorized_code` फ्लो का उपयोग करेंगे।** हमने यह दृष्टिकोण
चुना क्योंकि यह सरल है और हमारे विशिष्ट उपयोग के मामले के लिए एक अधिक सीधा यूजर अनुभव
प्रदान करता है: एक यूजर सीधे Issuer की अपनी वेबसाइट से एक क्रेडेंशियल का अनुरोध कर रहा है।
यह जटिल रीडायरेक्ट और क्लाइंट पंजीकरण की आवश्यकता को समाप्त करता है, जिससे मुख्य जारी करने
के तर्क को समझना और लागू करना आसान हो जाता है।

मानकों का यह संयोजन हमें एक ऐसा जारीकर्ता बनाने की अनुमति देता है जो डिजिटल वॉलेट की एक
विस्तृत श्रृंखला के साथ संगत है और यूजर के लिए एक सुरक्षित, मानकीकृत प्रक्रिया सुनिश्चित
करता है।

### 2.2 टेक स्टैक विकल्प

अपने जारीकर्ता को बनाने के लिए, हम उसी मजबूत और आधुनिक टेक स्टैक का उपयोग करेंगे जिसका
हमने सत्यापनकर्ता के लिए उपयोग किया था, जिससे एक सुसंगत और उच्च-गुणवत्ता वाला डेवलपर अनुभव
सुनिश्चित होता है।

#### 2.2.1 भाषा: TypeScript

हम अपने फ्रंटएंड और बैकएंड दोनों कोड के लिए **TypeScript** का उपयोग करेंगे। इसकी स्टैटिक
टाइपिंग एक जारीकर्ता जैसे सुरक्षा-महत्वपूर्ण एप्लिकेशन में अमूल्य है, क्योंकि यह सामान्य
त्रुटियों को रोकने में मदद करता है और कोड की समग्र गुणवत्ता और रखरखाव में सुधार करता है।

#### 2.2.2 फ्रेमवर्क: Next.js

**Next.js** हमारी पसंद का फ्रेमवर्क है क्योंकि यह फुल-स्टैक एप्लिकेशन बनाने के लिए एक सहज,
एकीकृत अनुभव प्रदान करता है।

- **फ्रंटएंड के लिए:** हम [React](https://www.corbado.com/blog/react-passkeys) के साथ
  [Next.js](https://www.corbado.com/blog/nextjs-passkeys) का उपयोग यूजर इंटरफेस बनाने के लिए करेंगे जहां यूजर्स
  क्रेडेंशियल का अनुरोध करने के लिए अपना डेटा इनपुट कर सकते हैं।
- **बैकएंड के लिए:** हम सर्वर-साइड एंडपॉइंट बनाने के लिए **Next.js API रूट्स** का लाभ
  उठाएंगे जो [OpenID4VCI](https://www.corbado.com/glossary/openid4vci) फ्लो को संभालते हैं, क्रेडेंशियल ऑफ़र
  जेनरेट करने से लेकर अंतिम हस्ताक्षरित क्रेडेंशियल जारी करने तक।

#### 2.2.3 प्रमुख लाइब्रेरीज़

हमारा कार्यान्वयन विशिष्ट कार्यों को संभालने के लिए कुछ प्रमुख लाइब्रेरीज़ पर निर्भर
करेगा:

- **next**, **react**, और **react-dom**: हमारे [Next.js](https://www.corbado.com/blog/nextjs-passkeys) एप्लिकेशन
  के लिए कोर लाइब्रेरीज़।
- **mysql2**: [Node.js](https://www.corbado.com/blog/nodejs-passkeys) के लिए एक
  [MySQL](https://www.corbado.com/blog/passkey-webauthn-database-guide) क्लाइंट, जिसका उपयोग प्राधिकरण कोड और
  सत्र डेटा को संग्रहीत करने के लिए किया जाता है।
- **uuid**: अद्वितीय पहचानकर्ता उत्पन्न करने के लिए एक लाइब्रेरी, जिसका उपयोग हम
  `pre-authorized_code` मान बनाने के लिए करेंगे।
- **jose**: JSON वेब सिग्नेचर (JWS) को संभालने के लिए एक मजबूत लाइब्रेरी, जिसका उपयोग हम
  जारी किए गए क्रेडेंशियल पर क्रिप्टोग्राफ़िक रूप से हस्ताक्षर करने के लिए करेंगे।

### 2.3 एक टेस्ट वॉलेट प्राप्त करें

अपने जारीकर्ता का परीक्षण करने के लिए, आपको एक मोबाइल वॉलेट की आवश्यकता होगी जो OpenID4VCI
प्रोटोकॉल का समर्थन करता हो। इस ट्यूटोरियल के लिए, हम **Sphereon Wallet** की सलाह देते
हैं, जो [Android](https://www.corbado.com/blog/how-to-enable-passkeys-android) और
[iOS](https://www.corbado.com/blog/how-to-enable-passkeys-ios) दोनों के लिए उपलब्ध है।

**Sphereon Wallet कैसे इंस्टॉल करें:**

1. **वॉलेट डाउनलोड करें**
   [Google Play Store](https://play.google.com/store/apps/details?id=com.sphereon.ssi.wallet)
   या [Apple App Store](https://apps.apple.com/us/app/sphereon-wallet/id1661096796) से।
2. ऐप को अपने मोबाइल डिवाइस पर इंस्टॉल करें।
3. एक बार इंस्टॉल हो जाने पर, वॉलेट QR कोड स्कैन करके क्रेडेंशियल ऑफ़र प्राप्त करने के लिए
   तैयार है।

### 2.4 क्रिप्टोग्राफ़ी ज्ञान

एक क्रेडेंशियल जारी करना एक सुरक्षा-महत्वपूर्ण ऑपरेशन है जो विश्वास और प्रामाणिकता
सुनिश्चित करने के लिए मौलिक क्रिप्टोग्राफ़िक अवधारणाओं पर निर्भर करता है।

#### 2.4.1 डिजिटल हस्ताक्षर

अपने मूल में, एक वेरिफ़ाएबल क्रेडेंशियल दावों का एक सेट है जिसे Issuer द्वारा **डिजिटल रूप
से हस्ताक्षरित** किया गया है। यह हस्ताक्षर दो गारंटी प्रदान करता है:

- **प्रामाणिकता:** यह साबित करता है कि क्रेडेंशियल एक वैध Issuer द्वारा बनाया गया था।
- **अखंडता:** यह साबित करता है कि क्रेडेंशियल जारी होने के बाद से उसके साथ कोई छेड़छाड़
  नहीं की गई है।

#### 2.4.2 पब्लिक/प्राइवेट की क्रिप्टोग्राफ़ी

डिजिटल हस्ताक्षर पब्लिक/प्राइवेट की क्रिप्टोग्राफ़ी का उपयोग करके बनाए जाते हैं। यहाँ यह
कैसे काम करता है:

1. **Issuer के पास एक की-पेयर होता है:** एक **प्राइवेट की**, जिसे गुप्त और सुरक्षित रखा
   जाता है, और एक संबंधित **पब्लिक की**, जिसे सार्वजनिक रूप से उपलब्ध कराया जाता है।
2. **हस्ताक्षर करना:** जब Issuer एक क्रेडेंशियल बनाता है, तो वह क्रेडेंशियल डेटा के लिए एक
   अद्वितीय डिजिटल हस्ताक्षर उत्पन्न करने के लिए अपनी **प्राइवेट की** का उपयोग करता है।
3. **सत्यापन:** एक Verifier बाद में हस्ताक्षर की जांच के लिए Issuer की **पब्लिक की** का
   उपयोग कर सकता है। यदि जांच पास हो जाती है, तो Verifier जानता है कि क्रेडेंशियल
   प्रामाणिक है और उसमें कोई बदलाव नहीं किया गया है।

हमारे कार्यान्वयन में, हम एक एलिप्टिक कर्व (EC) की-पेयर उत्पन्न करेंगे और JWT-VC पर
हस्ताक्षर करने के लिए `ES256` एल्गोरिथ्म का उपयोग करेंगे। पब्लिक की Issuer के DID
(`did:web`) में एम्बेडेड है, जिससे कोई भी Verifier इसे खोज सकता है और क्रेडेंशियल के
हस्ताक्षर को मान्य कर सकता है। **नोट:** हमारे JWTs में `aud` (ऑडियंस) क्लेम को जानबूझकर
छोड़ दिया गया है, क्योंकि क्रेडेंशियल को सामान्य-उद्देश्य के लिए डिज़ाइन किया गया है और यह
किसी विशिष्ट वॉलेट से बंधा नहीं है। यदि आप किसी विशेष ऑडियंस के लिए उपयोग को प्रतिबंधित
करना चाहते हैं, तो एक `aud` क्लेम शामिल करें और इसे तदनुसार सेट करें।

## 3. आर्किटेक्चरल अवलोकन

हमारा Issuer एप्लिकेशन एक फुल-स्टैक Next.js प्रोजेक्ट के रूप में बनाया गया है, जिसमें
फ्रंटएंड और बैकएंड लॉजिक के बीच एक स्पष्ट अलगाव है। यह आर्किटेक्चर हमें सर्वर पर सभी
सुरक्षा-महत्वपूर्ण कार्यों को संभालते हुए एक सहज यूजर अनुभव बनाने की अनुमति देता है।
**महत्वपूर्ण:** SQL में शामिल `verification_sessions` और `verified_credentials` टेबल इस
जारीकर्ता के लिए आवश्यक नहीं हैं, लेकिन पूर्णता के लिए शामिल की गई हैं।

- **फ्रंटएंड (`src/app/issue/page.tsx`):** एक एकल [React](https://www.corbado.com/blog/react-passkeys) पेज जो
  यूजर्स को क्रेडेंशियल का अनुरोध करने के लिए अपना डेटा इनपुट करने की अनुमति देता है। यह
  जारी करने की प्रक्रिया शुरू करने के लिए हमारे बैकएंड पर API कॉल करता है।
- **बैकएंड API रूट्स (`src/app/api/issue/...`):** सर्वर-साइड एंडपॉइंट्स का एक सेट जो
  OpenID4VCI प्रोटोकॉल को लागू करता है।
    - `/.well-known/openid-credential-issuer`: एक सार्वजनिक मेटाडेटा एंडपॉइंट। यह पहला URL
      है जिसे एक वॉलेट जारीकर्ता की क्षमताओं की खोज के लिए जांचेगा, जिसमें उसका प्राधिकरण
      सर्वर, टोकन एंडपॉइंट, क्रेडेंशियल एंडपॉइंट, और उन क्रेडेंशियल्स के प्रकार शामिल हैं
      जो यह प्रदान करता है।
    - `/.well-known/openid-configuration`: एक मानक OpenID कनेक्ट डिस्कवरी एंडपॉइंट। जबकि
      ऊपर वाले से निकटता से संबंधित है, यह एंडपॉइंट व्यापक OIDC-संबंधित कॉन्फ़िगरेशन
      परोसता है और अक्सर मानक OpenID क्लाइंट के साथ इंटरऑपरेबिलिटी के लिए आवश्यक होता है।
    - `/.well-known/did.json`: हमारे जारीकर्ता के लिए DID दस्तावेज़। `did:web` विधि का
      उपयोग करते समय, इस फ़ाइल का उपयोग जारीकर्ता की सार्वजनिक कुंजी प्रकाशित करने के लिए
      किया जाता है, जिसका उपयोग सत्यापनकर्ता इसके द्वारा जारी किए गए क्रेडेंशियल के
      हस्ताक्षर को मान्य करने के लिए कर सकते हैं।
    - `authorize/route.ts`: एक `pre-authorized_code` और एक क्रेडेंशियल ऑफ़र बनाता है।
    - `token/route.ts`: `pre-authorized_code` को एक एक्सेस टोकन के लिए एक्सचेंज करता है।
    - `credential/route.ts`: अंतिम, क्रिप्टोग्राफ़िक रूप से हस्ताक्षरित JWT-VC जारी करता
      है।
    - `schemas/pid/route.ts`: PID क्रेडेंशियल के लिए JSON स्कीमा को उजागर करता है। यह
      क्रेडेंशियल के किसी भी उपभोक्ता को इसकी संरचना और डेटा प्रकारों को समझने की अनुमति
      देता है।
- **लाइब्रेरी (`src/lib/`):**
    - `database.ts`: सभी डेटाबेस इंटरैक्शन का प्रबंधन करता है, जैसे प्राधिकरण कोड और
      जारीकर्ता कुंजी संग्रहीत करना।
    - `crypto.ts`: सभी क्रिप्टोग्राफ़िक कार्यों को संभालता है, जिसमें कुंजी पीढ़ी और JWT
      हस्ताक्षर शामिल हैं।

यहाँ जारी करने के प्रवाह को दर्शाने वाला एक आरेख है:

![Digital Credential Issuance Flow](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/Mermaid_Chart_Create_complex_visual_diagrams_with_text_A_smarter_way_of_creating_diagrams_2025_07_29_145228_d28fd13731.svg)

## 4. Issuer का निर्माण

अब जब हमें मानकों, प्रोटोकॉल और आर्किटेक्चर की ठोस समझ हो गई है, तो हम अपने जारीकर्ता का
निर्माण शुरू कर सकते हैं।

> **साथ चलें या अंतिम कोड का उपयोग करें**
>
> अब हम सेटअप और कोड कार्यान्वयन के माध्यम से स्टेप-बाय-स्टेप चलेंगे। यदि आप सीधे तैयार
> उत्पाद पर जाना पसंद करते हैं, तो आप हमारे GitHub रिपॉजिटरी से पूर्ण प्रोजेक्ट को क्लोन
> कर सकते हैं और इसे स्थानीय रूप से चला सकते हैं।
>
> ```bash
> git clone https://github.com/corbado/digital-credentials-example.git
> ```

### 4.1 प्रोजेक्ट की स्थापना

सबसे पहले, हम एक नया Next.js प्रोजेक्ट शुरू करेंगे, आवश्यक निर्भरताएँ स्थापित करेंगे, और
अपना डेटाबेस शुरू करेंगे।

#### 4.1.1 Next.js ऐप को इनिशियलाइज़ करना

अपना टर्मिनल खोलें, उस डायरेक्टरी में नेविगेट करें जहाँ आप अपना प्रोजेक्ट बनाना चाहते हैं,
और निम्नलिखित कमांड चलाएँ। हम इस प्रोजेक्ट के लिए ऐप राउटर, TypeScript, और Tailwind CSS का
उपयोग कर रहे हैं।

```bash
npx create-next-app@latest . --ts --eslint --tailwind --app --src-dir --import-alias "@/*" --use-npm
```

यह कमांड आपकी वर्तमान डायरेक्टरी में एक नया Next.js एप्लिकेशन बनाता है।

#### 4.1.2 निर्भरताएँ स्थापित करना

इसके बाद, हमें उन लाइब्रेरीज़ को स्थापित करने की आवश्यकता है जो JWTs, डेटाबेस कनेक्शन और
UUID पीढ़ी को संभालेंगी।

```bash
npm install jose mysql2 uuid @types/uuid
```

यह कमांड इंस्टॉल करता है:

- `jose`: JSON वेब टोकन (JWTs) पर हस्ताक्षर करने और सत्यापित करने के लिए।
- `mysql2`: हमारे डेटाबेस के लिए [MySQL](https://www.corbado.com/blog/passkey-webauthn-database-guide) क्लाइंट।
- `uuid`: अद्वितीय चुनौती स्ट्रिंग उत्पन्न करने के लिए।
- `@types/uuid`: `uuid` लाइब्रेरी के लिए TypeScript प्रकार।

#### 4.1.3 डेटाबेस शुरू करना

हमारे बैकएंड को प्राधिकरण कोड, जारी करने के सत्र और जारीकर्ता कुंजी संग्रहीत करने के लिए
एक [MySQL](https://www.corbado.com/blog/passkey-webauthn-database-guide) डेटाबेस की आवश्यकता होती है। हमने इसे
आसान बनाने के लिए एक `docker-compose.yml` फ़ाइल शामिल की है।

यदि आपने रिपॉजिटरी को क्लोन किया है, तो आप बस `docker-compose up -d` चला सकते हैं। यदि आप
स्क्रैच से बना रहे हैं, तो `docker-compose.yml` नामक एक फ़ाइल बनाएं जिसमें निम्नलिखित
सामग्री हो:

```yaml
services:
    mysql:
        image: mysql:8.0
        restart: always
        environment:
            MYSQL_ROOT_PASSWORD: rootpassword
            MYSQL_DATABASE: digital_credentials
            MYSQL_USER: app_user
            MYSQL_PASSWORD: app_password
        ports:
            - "3306:3306"
        volumes:
            - mysql_data:/var/lib/mysql
            - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql
        healthcheck:
            test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
            timeout: 20s
            retries: 10

volumes:
    mysql_data:
```

इस Docker Compose सेटअप के लिए एक SQL इनिशियलाइज़ेशन स्क्रिप्ट की भी आवश्यकता होती है।
`sql` नामक एक डायरेक्टरी बनाएं और उसके अंदर, `init.sql` नामक एक फ़ाइल बनाएं जिसमें
सत्यापनकर्ता और जारीकर्ता दोनों के लिए आवश्यक टेबल सेट करने के लिए निम्नलिखित सामग्री हो:

```sql
-- Create database if not exists
CREATE DATABASE IF NOT EXISTS digital_credentials;
USE digital_credentials;

-- Table for storing challenges
CREATE TABLE IF NOT EXISTS challenges (
    id VARCHAR(36) PRIMARY KEY,
    challenge VARCHAR(255) NOT NULL UNIQUE,
    expires_at TIMESTAMP NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    used BOOLEAN DEFAULT FALSE,
    INDEX idx_challenge (challenge),
    INDEX idx_expires_at (expires_at)
);

-- Table for storing verification sessions
CREATE TABLE IF NOT EXISTS verification_sessions (
    id VARCHAR(36) PRIMARY KEY,
    challenge_id VARCHAR(36),
    status ENUM('pending', 'verified', 'failed', 'expired') DEFAULT 'pending',
    presentation_data JSON,
    verified_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (challenge_id) REFERENCES challenges(id) ON DELETE CASCADE,
    INDEX idx_challenge_id (challenge_id),
    INDEX idx_status (status)
);

-- Table for storing verified credentials data (optional)
CREATE TABLE IF NOT EXISTS verified_credentials (
    id VARCHAR(36) PRIMARY KEY,
    session_id VARCHAR(36),
    credential_type VARCHAR(255),
    issuer VARCHAR(255),
    subject VARCHAR(255),
    claims JSON,
    verified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (session_id) REFERENCES verification_sessions(id) ON DELETE CASCADE,
    INDEX idx_session_id (session_id),
    INDEX idx_credential_type (credential_type)
);

-- ISSUER TABLES

-- Table for storing authorization codes in OpenID4VCI flow
CREATE TABLE IF NOT EXISTS authorization_codes (
    id VARCHAR(36) PRIMARY KEY,
    code VARCHAR(255) NOT NULL UNIQUE,
    client_id VARCHAR(255),
    scope VARCHAR(255),
    code_challenge VARCHAR(255),
    code_challenge_method VARCHAR(50),
    redirect_uri TEXT,
    user_pin VARCHAR(10),
    expires_at TIMESTAMP NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    used BOOLEAN DEFAULT FALSE,
    INDEX idx_code (code),
    INDEX idx_expires_at (expires_at)
);

-- Table for storing issuance sessions
CREATE TABLE IF NOT EXISTS issuance_sessions (
    id VARCHAR(36) PRIMARY KEY,
    authorization_code_id VARCHAR(36),
    access_token VARCHAR(255),
    token_type VARCHAR(50) DEFAULT 'Bearer',
    expires_in INT DEFAULT 3600,
    c_nonce VARCHAR(255),
    c_nonce_expires_at TIMESTAMP,
    status ENUM('pending', 'authorized', 'credential_issued', 'expired', 'failed') DEFAULT 'pending',
    user_data JSON,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (authorization_code_id) REFERENCES authorization_codes(id) ON DELETE CASCADE,
    INDEX idx_access_token (access_token),
    INDEX idx_c_nonce (c_nonce),
    INDEX idx_status (status)
);

-- Table for storing issued credentials
CREATE TABLE IF NOT EXISTS issued_credentials (
    id VARCHAR(36) PRIMARY KEY,
    session_id VARCHAR(36),
    credential_id VARCHAR(255),
    credential_type VARCHAR(255) DEFAULT 'jwt_vc',
    doctype VARCHAR(255) DEFAULT 'eu.europa.ec.eudi.pid.1',
    credential_data LONGTEXT, -- Base64 encoded mDoc
    credential_claims JSON,
    issuer_did VARCHAR(255),
    subject_id VARCHAR(255),
    issued_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    expires_at TIMESTAMP,
    revoked BOOLEAN DEFAULT FALSE,
    revoked_at TIMESTAMP NULL,
    FOREIGN KEY (session_id) REFERENCES issuance_sessions(id) ON DELETE CASCADE,
    INDEX idx_credential_id (credential_id),
    INDEX idx_session_id (session_id),
    INDEX idx_doctype (doctype),
    INDEX idx_subject_id (subject_id),
    INDEX idx_issued_at (issued_at)
);

-- Table for storing issuer keys (simplified for demo)
CREATE TABLE IF NOT EXISTS issuer_keys (
    id VARCHAR(36) PRIMARY KEY,
    key_id VARCHAR(255) NOT NULL UNIQUE,
    key_type VARCHAR(50) NOT NULL, -- 'EC', 'RSA'
    algorithm VARCHAR(50) NOT NULL, -- 'ES256', 'RS256', etc.
    public_key TEXT NOT NULL, -- JWK format
    private_key TEXT NOT NULL, -- JWK format (encrypted in production)
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_key_id (key_id),
    INDEX idx_is_active (is_active)
);
```

एक बार दोनों फ़ाइलें जगह पर हों, तो अपने प्रोजेक्ट रूट में अपना टर्मिनल खोलें और चलाएँ:

```bash
docker-compose up -d
```

यह कमांड बैकग्राउंड में एक MySQL कंटेनर शुरू करेगा, जो हमारे एप्लिकेशन के उपयोग के लिए
तैयार है।

### 4.2 साझा लाइब्रेरीज़ को लागू करना

API एंडपॉइंट बनाने से पहले, आइए साझा लाइब्रेरीज़ बनाएं जो कोर बिज़नेस लॉजिक को संभालेंगी।
यह दृष्टिकोण हमारे API रूट्स को साफ और HTTP अनुरोधों को संभालने पर केंद्रित रखता है, जबकि
जटिल काम इन मॉड्यूल को सौंप दिया जाता है।

#### 4.2.1 डेटाबेस लाइब्रेरी (`src/lib/database.ts`)

यह फ़ाइल सभी डेटाबेस इंटरैक्शन के लिए सत्य का एकल स्रोत है। यह हमारे MySQL कंटेनर से
कनेक्ट करने के लिए `mysql2` लाइब्रेरी का उपयोग करती है और हमारी तालिकाओं में रिकॉर्ड
बनाने, पढ़ने और अपडेट करने के लिए निर्यात किए गए कार्यों का एक सेट प्रदान करती है। यह
एब्स्ट्रैक्शन लेयर हमारे कोड को अधिक मॉड्यूलर और बनाए रखने में आसान बनाती है।

`src/lib/database.ts` फ़ाइल को निम्नलिखित सामग्री के साथ बनाएं:

```typescript
// src/lib/database.ts
import mysql from "mysql2/promise";

// Database connection configuration
const dbConfig = {
    host: process.env.DATABASE_HOST || "localhost",
    port: parseInt(process.env.DATABASE_PORT || "3306"),
    user: process.env.DATABASE_USER || "app_user",
    password: process.env.DATABASE_PASSWORD || "app_password",
    database: process.env.DATABASE_NAME || "digital_credentials",
    timezone: "+00:00",
};

let connection: mysql.Connection | null = null;

export async function getConnection(): Promise<mysql.Connection> {
    if (!connection) {
        connection = await mysql.createConnection(dbConfig);
    }
    return connection;
}

// Data-Access-Object (DAO) functions for each table
// ... (e.g., createChallenge, getChallenge, createAuthorizationCode, etc.)
```

> **नोट:** संक्षिप्तता के लिए, DAO कार्यों की पूरी सूची को छोड़ दिया गया है। आप पूरा कोड
> [प्रोजेक्ट रिपॉजिटरी](https://github.com/corbado/digital-credentials-example/blob/main/src/lib/database.ts)
> में पा सकते हैं। इस फ़ाइल में चुनौतियों, सत्यापन सत्रों, प्राधिकरण कोड, जारी करने के
> सत्रों और जारीकर्ता कुंजी के प्रबंधन के लिए कार्य शामिल हैं।

#### 4.2.2 क्रिप्टो लाइब्रेरी (`src/lib/crypto.ts`)

यह फ़ाइल सभी सुरक्षा-महत्वपूर्ण क्रिप्टोग्राफ़िक कार्यों को संभालती है। यह की-पेयर जेनरेट
करने और JSON वेब टोकन (JWTs) पर हस्ताक्षर करने के लिए `jose` लाइब्रेरी का उपयोग करती है।

**की जेनरेशन** `generateIssuerKeyPair` फ़ंक्शन एक नया एलिप्टिक कर्व की-पेयर बनाता है जिसका
उपयोग क्रेडेंशियल पर हस्ताक्षर करने के लिए किया जाएगा। पब्लिक की JSON वेब की (JWK) प्रारूप
में निर्यात की जाती है ताकि इसे हमारे `did.json` दस्तावेज़ में प्रकाशित किया जा सके।

```typescript
// src/lib/crypto.ts
import { generateKeyPair, exportJWK, SignJWT } from "jose";

export async function generateIssuerKeyPair(keyId: string, issuerDid: string) {
    const { publicKey, privateKey } = await generateKeyPair("ES256", {
        crv: "P-256",
        extractable: true,
    });

    const publicKeyJWK = await exportJWK(publicKey);
    publicKeyJWK.kid = keyId; // Assign a unique key ID

    // ... (private key export and other setup)

    return { publicKey, privateKey, publicKeyJWK /* ... */ };
}
```

**JWT क्रेडेंशियल निर्माण** `createJWTVerifiableCredential` फ़ंक्शन जारी करने की प्रक्रिया
का मूल है। यह यूजर के दावों, जारीकर्ता के की-पेयर, और अन्य मेटाडेटा को लेता है, और उनका
उपयोग एक हस्ताक्षरित JWT-VC बनाने के लिए करता है।

```typescript
// src/lib/crypto.ts

export async function createJWTVerifiableCredential(
    claims: MDocClaims,
    issuerKeyPair: IssuerKeyPair,
    subjectId: string,
    audience: string,
): Promise<string> {
    const now = Math.floor(Date.now() / 1000);
    const oneYear = 365 * 24 * 60 * 60;

    const vcPayload = {
        // The issuer's DID
        iss: issuerKeyPair.issuerDid,
        // The subject's (holder's) DID
        sub: subjectId,
        // The time the credential was issued (iat) and when it expires (exp)
        iat: now,
        exp: now + oneYear,
        // The Verifiable Credential data model
        vc: {
            "@context": [
                "https://www.w3.org/2018/credentials/v1",
                "https://europa.eu/eudi/pid/v1",
            ],
            type: ["VerifiableCredential", "eu.europa.ec.eudi.pid.1"],
            issuer: issuerKeyPair.issuerDid,
            issuanceDate: new Date(now * 1000).toISOString(),
            credentialSubject: {
                id: subjectId,
                ...claims,
            },
        },
    };

    // Sign the payload with the issuer's private key
    return await new SignJWT(vcPayload)
        .setProtectedHeader({
            alg: issuerKeyPair.algorithm,
            kid: issuerKeyPair.keyId,
            typ: "JWT",
        })
        .sign(issuerKeyPair.privateKey);
}
```

यह फ़ंक्शन W3C वेरिफ़ाएबल क्रेडेंशियल डेटा मॉडल के अनुसार JWT पेलोड का निर्माण करता है और
इसे जारीकर्ता की निजी कुंजी के साथ हस्ताक्षरित करता है, जिससे एक सुरक्षित और वेरिफ़ाएबल
क्रेडेंशियल बनता है।

### 4.2 Next.js ऐप का आर्किटेक्चरल अवलोकन

हमारा Next.js एप्लिकेशन फ्रंटएंड और बैकएंड के बीच चिंताओं को अलग करने के लिए संरचित है,
भले ही वे एक ही प्रोजेक्ट का हिस्सा हों। यह UI पेजों और API एंडपॉइंट्स दोनों के लिए ऐप
राउटर का लाभ उठाकर हासिल किया जाता है।

- **फ्रंटएंड (`src/app/issue/page.tsx`):** एक एकल [React](https://www.corbado.com/blog/react-passkeys) पेज
  कंपोनेंट जो `/issue` रूट के लिए UI को परिभाषित करता है। यह यूजर इनपुट को संभालता है और
  हमारे बैकएंड API के साथ संचार करता है।

- **बैकएंड API रूट्स (`src/app/api/...`):**
    - **डिस्कवरी (`.well-known/.../route.ts`):** ये रूट्स सार्वजनिक मेटाडेटा एंडपॉइंट्स को
      उजागर करते हैं जो वॉलेट और अन्य क्लाइंट को जारीकर्ता की क्षमताओं और सार्वजनिक कुंजी
      की खोज करने की अनुमति देते हैं।
    - **जारी करना (`issue/.../route.ts`):** ये एंडपॉइंट्स कोर OpenID4VCI लॉजिक को लागू
      करते हैं, जिसमें क्रेडेंशियल ऑफ़र बनाना, टोकन जारी करना और अंतिम क्रेडेंशियल पर
      हस्ताक्षर करना शामिल है।
    - **स्कीमा (`schemas/pid/route.ts`):** यह रूट क्रेडेंशियल के लिए JSON स्कीमा परोसता
      है, इसकी संरचना को परिभाषित करता है।

- **लाइब्रेरी (`src/lib/`):** इस डायरेक्टरी में बैकएंड में साझा किया गया पुन: प्रयोज्य
  लॉजिक होता है।
    - `database.ts`: सभी डेटाबेस इंटरैक्शन का प्रबंधन करता है, SQL क्वेरी को एब्स्ट्रैक्ट
      करता है।
    - `crypto.ts`: सभी क्रिप्टोग्राफ़िक कार्यों को संभालता है, जैसे की जेनरेशन और JWT
      हस्ताक्षर।

यह स्पष्ट अलगाव एप्लिकेशन को मॉड्यूलर और बनाए रखने में आसान बनाता है।

**नोट:** `generateIssuerDid()` फ़ंक्शन को आपके जारीकर्ता डोमेन से मेल खाने वाला एक मान्य
`did:web` लौटाना होगा। तैनात होने पर, `.well-known/did.json` को उस डोमेन पर HTTPS पर परोसा
जाना चाहिए ताकि सत्यापनकर्ता क्रेडेंशियल को मान्य कर सकें।

![Architectural overview of Next.js App](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/Mermaid_Chart_Create_complex_visual_diagrams_with_text_A_smarter_way_of_creating_diagrams_2025_07_29_151549_6a0aca6477.svg)

### 4.3 फ्रंटएंड का निर्माण

हमारा फ्रंटएंड एक एकल React पेज है जो यूजर्स को एक नया डिजिटल क्रेडेंशियल अनुरोध करने के
लिए एक सरल फ़ॉर्म प्रदान करता है। इसकी जिम्मेदारियां हैं:

- यूजर डेटा (नाम, जन्म तिथि, आदि) कैप्चर करना।
- एक क्रेडेंशियल ऑफ़र बनाने के लिए इस डेटा को हमारे बैकएंड पर भेजना।
- परिणामी QR कोड और पिन को यूजर के लिए प्रदर्शित करना ताकि वे अपने वॉलेट से स्कैन कर सकें।

कोर लॉजिक `handleSubmit` फ़ंक्शन में संभाला जाता है, जो तब ट्रिगर होता है जब यूजर फ़ॉर्म
सबमिट करता है।

```typescript
// src/app/issue/page.tsx

const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);
    setError(null);
    setCredentialOffer(null);

    try {
        // 1. Validate required fields
        if (!userData.given_name || !userData.family_name || !userData.birth_date) {
            throw new Error("Please fill in all required fields");
        }

        // 2. Request a credential offer from the backend
        const response = await fetch("/api/issue/authorize", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_data: userData,
            }),
        });

        if (!response.ok) {
            const errorData = await response.json();
            throw new Error(
                errorData.error_description || "Failed to create credential offer",
            );
        }

        // 3. Set the credential offer in state to display the QR code
        const result = await response.json();
        setCredentialOffer(result);
    } catch (err) {
        const errorMessage = (err as Error).message || "Unknown error occurred";
        setError(errorMessage);
    } finally {
        setLoading(false);
    }
};
```

यह फ़ंक्शन तीन प्रमुख क्रियाएं करता है:

1. **फॉर्म डेटा को मान्य करता है** ताकि यह सुनिश्चित हो सके कि सभी आवश्यक फ़ील्ड भरे हुए
   हैं।
2. हमारे `/api/issue/authorize` एंडपॉइंट पर यूजर के डेटा के साथ एक **`POST` अनुरोध भेजता
   है**।
3. बैकएंड से प्राप्त क्रेडेंशियल ऑफ़र के साथ **कंपोनेंट की स्थिति को अपडेट करता है**, जो
   UI को QR कोड और लेनदेन कोड प्रदर्शित करने के लिए ट्रिगर करता है।

फ़ाइल के बाकी हिस्से में फ़ॉर्म और QR कोड डिस्प्ले को रेंडर करने के लिए मानक React कोड
होता है। आप पूरी फ़ाइल
[प्रोजेक्ट रिपॉजिटरी](https://github.com/corbado/digital-credentials-example/blob/main/src/app/issue/page.tsx)
में देख सकते हैं।

### 4.4 पर्यावरण और डिस्कवरी की स्थापना

बैकएंड API बनाने से पहले, हमें अपने पर्यावरण को कॉन्फ़िगर करने और डिस्कवरी एंडपॉइंट्स
स्थापित करने की आवश्यकता है। ये `.well-known` फ़ाइलें वॉलेट के लिए हमारे जारीकर्ता को
खोजने और उसके साथ कैसे इंटरैक्ट करना है, यह समझने के लिए महत्वपूर्ण हैं।

#### 4.4.1 पर्यावरण फ़ाइल बनाएँ

अपने प्रोजेक्ट के रूट में `.env.local` नामक एक फ़ाइल बनाएँ और निम्नलिखित पंक्ति जोड़ें। एक
मोबाइल वॉलेट तक पहुंचने के लिए यह URL सार्वजनिक रूप से सुलभ होना चाहिए। स्थानीय विकास के
लिए, आप अपने `localhost` को उजागर करने के लिए
[ngrok](https://www.corbado.com/blog/multi-device-passkey-login-corbado-ngrok) जैसी टनलिंग सेवा का उपयोग कर सकते
हैं।

```
NEXT_PUBLIC_BASE_URL=http://localhost:3000
```

#### 4.4.2 डिस्कवरी एंडपॉइंट्स को लागू करें

वॉलेट मानक `.well-known` URLs को क्वेरी करके एक जारीकर्ता की क्षमताओं की खोज करते हैं।
हमें इनमें से तीन एंडपॉइंट बनाने की आवश्यकता है।

**1. जारीकर्ता मेटाडेटा (`/.well-known/openid-credential-issuer`)**

यह OpenID4VCI के लिए प्राथमिक डिस्कवरी फ़ाइल है। यह वॉलेट को जारीकर्ता के बारे में वह सब
कुछ बताती है जो उसे जानने की जरूरत है, जिसमें उसके एंडपॉइंट्स, वह किस प्रकार के
क्रेडेंशियल प्रदान करता है, और समर्थित क्रिप्टोग्राफ़िक एल्गोरिदम शामिल हैं।

फ़ाइल `src/app/.well-known/openid-credential-issuer/route.ts` बनाएं:

```typescript
// src/app/.well-known/openid-credential-issuer/route.ts
import { NextResponse } from "next/server";

export async function GET() {
    const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000";

    const issuerMetadata = {
        // The issuer's unique identifier.
        issuer: baseUrl,
        // The URL of the authorization server. For simplicity, our issuer is its own authorization server.
        authorization_servers: [baseUrl],
        // The URL of the credential issuer.
        credential_issuer: baseUrl,
        // The endpoint where the wallet will POST to receive the actual credential.
        credential_endpoint: `${baseUrl}/api/issue/credential`,
        // The endpoint where the wallet exchanges an authorization code for an access token.
        token_endpoint: `${baseUrl}/api/issue/token`,
        // The endpoint for the authorization flow (not used in our pre-authorized flow, but good practice to include).
        authorization_endpoint: `${baseUrl}/api/issue/authorize`,
        // Indicates support for the pre-authorized code flow without requiring client authentication.
        pre_authorized_grant_anonymous_access_supported: true,
        // Human-readable information about the issuer.
        display: [
            {
                name: "Corbado Credentials Issuer",
                locale: "en-US",
            },
        ],
        // A list of the credential types this issuer can issue.
        credential_configurations_supported: {
            "eu.europa.ec.eudi.pid.1": {
                // The format of the credential (e.g., jwt_vc, mso_mdoc).
                format: "jwt_vc",
                // The specific document type, conforming to ISO mDoc standards.
                doctype: "eu.europa.ec.eudi.pid.1",
                // The OAuth 2.0 scope associated with this credential type.
                scope: "eu.europa.ec.eudi.pid.1",
                // Methods the wallet can use to prove possession of its key.
                cryptographic_binding_methods_supported: ["jwk"],
                // Signing algorithms the issuer supports for this credential.
                credential_signing_alg_values_supported: ["ES256"],
                // Proof-of-possession types the wallet can use.
                proof_types_supported: {
                    jwt: {
                        proof_signing_alg_values_supported: ["ES256", "ES384", "ES512"],
                    },
                },
                // Display properties for the credential.
                display: [
                    {
                        name: "Corbado Credential Issuer",
                        locale: "en-US",
                        logo: {
                            uri: `${baseUrl}/logo.png`,
                            alt_text: "EU Digital Identity",
                        },
                        background_color: "#003399",
                        text_color: "#FFFFFF",
                    },
                ],
                // A list of the claims (attributes) in the credential.
                claims: {
                    "eu.europa.ec.eudi.pid.1": {
                        given_name: {
                            mandatory: true,
                            display: [{ name: "Given Name", locale: "en-US" }],
                        },
                        family_name: {
                            mandatory: true,
                            display: [{ name: "Family Name", locale: "en-US" }],
                        },
                        birth_date: {
                            mandatory: true,
                            display: [{ name: "Date of Birth", locale: "en-US" }],
                        },
                    },
                },
            },
        },
        // Authentication methods supported by the token endpoint. 'none' means public client.
        token_endpoint_auth_methods_supported: ["none"],
        // PKCE code challenge methods supported.
        code_challenge_methods_supported: ["S256"],
        // OAuth 2.0 grant types the issuer supports.
        grant_types_supported: [
            "authorization_code",
            "urn:ietf:params:oauth:grant-type:pre-authorized_code",
        ],
    };

    return NextResponse.json(issuerMetadata, {
        headers: {
            "Content-Type": "application/json",
            "Cache-Control": "no-cache, no-store, must-revalidate",
            Pragma: "no-cache",
            Expires: "0",
        },
    });
}
```

**2. OpenID कॉन्फ़िगरेशन (`/.well-known/openid-configuration`)**

यह एक मानक OIDC डिस्कवरी दस्तावेज़ है जो कॉन्फ़िगरेशन विवरणों का एक व्यापक सेट प्रदान करता
है।

फ़ाइल `src/app/.well-known/openid-configuration/route.ts` बनाएं:

```typescript
// src/app/.well-known/openid-configuration/route.ts
import { NextResponse } from "next/server";

export async function GET() {
    const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000";

    const openidConfiguration = {
        // The issuer's unique identifier.
        credential_issuer: baseUrl,
        // The endpoint where the wallet will POST to receive the actual credential.
        credential_endpoint: `${baseUrl}/api/issue/credential`,
        // The endpoint for the authorization flow.
        authorization_endpoint: `${baseUrl}/api/issue/authorize`,
        // The endpoint where the wallet exchanges an authorization code for an access token.
        token_endpoint: `${baseUrl}/api/issue/token`,
        // A list of the credential types this issuer can issue.
        credential_configurations_supported: {
            "eu.europa.ec.eudi.pid.1": {
                format: "jwt_vc",
                scope: "eu.europa.ec.eudi.pid.1",
                cryptographic_binding_methods_supported: ["jwk"],
                credential_signing_alg_values_supported: ["ES256", "ES384", "ES512"],
                proof_types_supported: {
                    jwt: {
                        proof_signing_alg_values_supported: ["ES256", "ES384", "ES512"],
                    },
                },
            },
        },
        // OAuth 2.0 grant types the issuer supports.
        grant_types_supported: [
            "authorization_code",
            "urn:ietf:params:oauth:grant-type:pre-authorized_code",
        ],
        // Indicates support for the pre-authorized code flow.
        pre_authorized_grant_anonymous_access_supported: true,
        // PKCE code challenge methods supported.
        code_challenge_methods_supported: ["S256"],
        // Authentication methods supported by the token endpoint.
        token_endpoint_auth_methods_supported: ["none"],
        // OAuth 2.0 scopes the issuer supports.
        scopes_supported: ["eu.europa.ec.eudi.pid.1"],
    };

    return NextResponse.json(openidConfiguration, {
        headers: {
            "Content-Type": "application/json",
            "Cache-Control": "no-cache, no-store, must-revalidate",
            Pragma: "no-cache",
            Expires: "0",
        },
    });
}
```

**3. DID दस्तावेज़ (`/.well-known/did.json`)**

यह फ़ाइल `did:web` विधि का उपयोग करके जारीकर्ता की सार्वजनिक कुंजी प्रकाशित करती है, जिससे
कोई भी इसके द्वारा जारी किए गए क्रेडेंशियल के हस्ताक्षर को सत्यापित कर सकता है।

फ़ाइल `src/app/.well-known/did.json/route.ts` बनाएं:

```typescript
// src/app/.well-known/did.json/route.ts
import { NextResponse } from "next/server";
import { getActiveIssuerKey } from "../../../lib/database";
import { generateIssuerDid } from "../../../lib/crypto";

export async function GET() {
    const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000";
    const issuerKey = await getActiveIssuerKey();

    if (!issuerKey) {
        return NextResponse.json(
            { error: "No active issuer key found" },
            { status: 404 },
        );
    }

    const publicKeyJWK = JSON.parse(issuerKey.public_key);
    const didId = generateIssuerDid();
    const didDocument = {
        // The context defines the vocabulary used in the document.
        "@context": [
            "https://www.w3.org/ns/did/v1",
            "https://w3id.org/security/suites/jws-2020/v1",
        ],
        // The DID URI, which is the unique identifier for the issuer.
        id: didId,
        // The DID controller, which is the entity that controls the DID. Here, it's the issuer itself.
        controller: didId,
        // A list of public keys that can be used to verify signatures from the issuer.
        verificationMethod: [
            {
                // A unique identifier for the key, scoped to the DID.
                id: `${didId}#${issuerKey.key_id}`,
                // The type of the key.
                type: "JsonWebKey2020",
                // The DID of the key's controller.
                controller: didId,
                // The public key in JWK format.
                publicKeyJwk: publicKeyJWK,
            },
        ],
        // Specifies which keys can be used for authentication (proving control of the DID).
        authentication: [`${didId}#${issuerKey.key_id}`],
        // Specifies which keys can be used for creating verifiable credentials.
        assertionMethod: [`${didId}#${issuerKey.key_id}`],
        // A list of services provided by the DID subject, such as the issuer endpoint.
        service: [
            {
                id: `${didId}#openid-credential-issuer`,
                type: "OpenIDCredentialIssuer",
                serviceEndpoint: `${baseUrl}/.well-known/openid-credential-issuer`,
            },
        ],
    };

    return NextResponse.json(didDocument, {
        headers: {
            "Content-Type": "application/did+json",
            "Cache-Control": "no-cache, no-store, must-revalidate",
            Pragma: "no-cache",
            Expires: "0",
        },
    });
}
```

> **कैशिंग क्यों नहीं?** आप देखेंगे कि ये तीनों एंडपॉइंट्स हेडर लौटाते हैं जो आक्रामक रूप
> से कैशिंग को रोकते हैं (`Cache-Control: no-cache`, `Pragma: no-cache`, `Expires: 0`)। यह
> डिस्कवरी दस्तावेज़ों के लिए एक महत्वपूर्ण सुरक्षा अभ्यास है। जारीकर्ता कॉन्फ़िगरेशन बदल
> सकते हैं-उदाहरण के लिए, एक क्रिप्टोग्राफ़िक कुंजी को घुमाया जा सकता है। यदि कोई वॉलेट या
> क्लाइंट `did.json` या `openid-credential-issuer` फ़ाइल के पुराने संस्करण को कैश करता है,
> तो यह नए क्रेडेंशियल को मान्य करने या अपडेट किए गए एंडपॉइंट्स के साथ इंटरैक्ट करने में
> विफल हो जाएगा। क्लाइंट को प्रत्येक अनुरोध पर एक ताज़ा प्रतिलिपि प्राप्त करने के लिए
> मजबूर करके, हम यह सुनिश्चित करते हैं कि उनके पास हमेशा सबसे अद्यतित जानकारी हो।

#### 4.4.3 क्रेडेंशियल स्कीमा एंडपॉइंट को लागू करें

हमारे सार्वजनिक-सामना करने वाले [बुनियादी ढांचे](https://www.corbado.com/passkeys-for-critical-infrastructure) का
अंतिम टुकड़ा क्रेडेंशियल स्कीमा एंडपॉइंट है। यह रूट एक JSON स्कीमा परोसता है जो औपचारिक
रूप से PID क्रेडेंशियल की संरचना, डेटा प्रकारों और बाधाओं को परिभाषित करता है जिसे हम जारी
कर रहे हैं। वॉलेट और सत्यापनकर्ता इस स्कीमा का उपयोग क्रेडेंशियल की सामग्री को मान्य करने
के लिए कर सकते हैं।

निम्नलिखित सामग्री के साथ `src/app/api/schemas/pid/route.ts` फ़ाइल बनाएं:

```typescript
// src/app/api/schemas/pid/route.ts
import { NextResponse } from "next/server";

export async function GET() {
    const schema = {
        $schema: "https://json-schema.org/draft/2020-12/schema",
        $id: "https://example.com/schemas/pid", // Replace with your actual domain
        title: "PID Credential",
        description:
            "A schema for a Verifiable Credential representing a Personal Identification Document (PID).",
        type: "object",
        properties: {
            credentialSubject: {
                type: "object",
                properties: {
                    given_name: { type: "string" },
                    family_name: { type: "string" },
                    birth_date: { type: "string", format: "date" },
                    // ... other properties of the credential subject
                },
                required: ["given_name", "family_name", "birth_date"],
            },
            // ... other top-level properties of a Verifiable Credential
        },
    };

    return NextResponse.json(schema, {
        headers: {
            "Content-Type": "application/schema+json",
            "Access-Control-Allow-Origin": "*", // Allow cross-origin requests
        },
    });
}
```

> **नोट:** एक PID क्रेडेंशियल के लिए JSON स्कीमा काफी बड़ा और विस्तृत हो सकता है।
> संक्षिप्तता के लिए, पूर्ण स्कीमा को छोटा कर दिया गया है। आप पूरी फ़ाइल
> [प्रोजेक्ट रिपॉजिटरी](https://github.com/corbado/digital-credentials-example/blob/main/src/app/api/schemas/pid/route.ts)
> में पा सकते हैं।

### 4.5 बैकएंड एंडपॉइंट्स का निर्माण

फ्रंटएंड के साथ, अब हमें OpenID4VCI प्रवाह को संभालने के लिए सर्वर-साइड लॉजिक की आवश्यकता
है। हम पहले एंडपॉइंट से शुरू करेंगे जिसे फ्रंटएंड कॉल करता है: `/api/issue/authorize`।

#### 4.5.1 `/api/issue/authorize`: क्रेडेंशियल ऑफ़र बनाएँ

यह एंडपॉइंट यूजर के डेटा को लेने, एक सुरक्षित वन-टाइम-यूज़ कोड जेनरेट करने, और एक
`credential_offer` बनाने के लिए ज़िम्मेदार है जिसे यूजर का वॉलेट समझ सकता है।

यहाँ कोर लॉजिक है:

```typescript
// src/app/api/issue/authorize/route.ts
import { NextRequest, NextResponse } from "next/server";
import { v4 as uuidv4 } from "uuid";
import { createAuthorizationCode } from "@/lib/database";

export async function POST(request: NextRequest) {
    try {
        const body = await request.json();
        const { user_data } = body;

        // 1. Validate user data
        if (
            !user_data ||
            !user_data.given_name ||
            !user_data.family_name ||
            !user_data.birth_date
        ) {
            return NextResponse.json({ error: "missing_user_data" }, { status: 400 });
        }

        // 2. Generate a pre-authorized code and a PIN
        const code = uuidv4();
        const expiresAt = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes
        const txCode = Math.floor(1000 + Math.random() * 9000).toString(); // 4-digit PIN

        // 3. Store the code and user data
        await createAuthorizationCode(uuidv4(), code, expiresAt);
        // Note: This uses an in-memory store for demo purposes only.
        // In production, persist data securely in a database with proper expiry.
        if (!(global as any).userDataStore) (global as any).userDataStore = new Map();
        (global as any).userDataStore.set(code, user_data);
        if (!(global as any).txCodeStore) (global as any).txCodeStore = new Map();
        (global as any).txCodeStore.set(code, txCode);

        // 4. Create the credential offer object
        const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000";
        const credentialOffer = {
            // The issuer's identifier, which is its base URL.
            credential_issuer: baseUrl,
            // An array of credential types the issuer is offering.
            credential_configuration_ids: ["eu.europa.ec.eudi.pid.1"],
            // Specifies the grant types the wallet can use.
            grants: {
                // We are using the pre-authorized code flow.
                "urn:ietf:params:oauth:grant-type:pre-authorized_code": {
                    // The one-time code the wallet will exchange for a token.
                    "pre-authorized_code": code,
                    // Indicates that the user must enter a PIN (tx_code) to redeem the code.
                    user_pin_required: true,
                },
            },
        };

        // 5. Create the full credential offer URI (a deep link for wallets)
        const credentialOfferUri = `openid-credential-offer://?credential_offer=${encodeURIComponent(
            JSON.stringify(credentialOffer),
        )}`;

        // The final response to the frontend.
        return NextResponse.json({
            // The deep link for the QR code.
            credential_offer_uri: credentialOfferUri,
            // The raw pre-authorized code, for display or manual entry.
            pre_authorized_code: code,
            // The 4-digit PIN the user must enter in their wallet.
            tx_code: txCode,
        });
    } catch (error) {
        console.error("Authorization error:", error);
        return NextResponse.json({ error: "server_error" }, { status: 500 });
    }
}
```

इस एंडपॉइंट में मुख्य चरण:

1. **डेटा मान्य करें:** यह पहले सुनिश्चित करता है कि आवश्यक यूजर डेटा मौजूद है।
2. **कोड जेनरेट करें:** यह एक अद्वितीय `pre-authorized_code` (एक UUID) और सुरक्षा की एक
   अतिरिक्त परत के लिए 4-अंकीय `tx_code` (PIN) बनाता है।
3. **डेटा बनाए रखें:** `pre-authorized_code` को डेटाबेस में एक छोटी समाप्ति समय के साथ
   संग्रहीत किया जाता है। यूजर का डेटा और पिन इन-मेमोरी में संग्रहीत होते हैं, जो कोड से
   जुड़े होते हैं।
4. **ऑफ़र बनाएँ:** यह OpenID4VCI विनिर्देश के अनुसार `credential_offer` ऑब्जेक्ट का
   निर्माण करता है। यह ऑब्जेक्ट वॉलेट को बताता है कि जारीकर्ता कहाँ है, वह कौन से
   क्रेडेंशियल प्रदान करता है, और उन्हें प्राप्त करने के लिए आवश्यक कोड क्या है।
5. **URI लौटाएँ:** अंत में, यह एक डीप लिंक URI (`openid-credential-offer://...`) बनाता है
   और इसे फ्रंटएंड को लौटाता है, साथ ही यूजर को देखने के लिए `tx_code` भी।

#### 4.5.2 `/api/issue/token`: कोड को टोकन के लिए एक्सचेंज करें

एक बार जब यूजर QR कोड को स्कैन करता है और अपना पिन दर्ज करता है, तो वॉलेट इस एंडपॉइंट पर
एक `POST` अनुरोध करता है। इसका काम `pre-authorized_code` और `user_pin` (PIN) को मान्य करना
है, और यदि वे मान्य हैं, तो एक अल्पकालिक एक्सेस टोकन जारी करना है।

निम्नलिखित सामग्री के साथ `src/app/api/issue/token/route.ts` फ़ाइल बनाएं:

```typescript
// src/app/api/issue/token/route.ts
import { NextRequest, NextResponse } from "next/server";
import { v4 as uuidv4 } from "uuid";
import {
    getAuthorizationCode,
    markAuthorizationCodeAsUsed,
    createIssuanceSession,
} from "@/lib/database";

export async function POST(request: NextRequest) {
    try {
        const formData = await request.formData();
        const grant_type = formData.get("grant_type") as string;
        const code = formData.get("pre-authorized_code") as string;
        const user_pin = formData.get("user_pin") as string;

        // 1. Validate the grant type
        if (grant_type !== "urn:ietf:params:oauth:grant-type:pre-authorized_code") {
            return NextResponse.json(
                { error: "unsupported_grant_type" },
                { status: 400 },
            );
        }

        // 2. Validate the pre-authorized code
        const authCode = await getAuthorizationCode(code);
        if (!authCode) {
            return NextResponse.json(
                {
                    error: "invalid_grant",
                    error_description: "Invalid or expired code",
                },
                { status: 400 },
            );
        }

        // 3. Validate the PIN (tx_code)
        const expectedTxCode = (global as any).txCodeStore?.get(code);
        if (expectedTxCode !== user_pin) {
            return NextResponse.json(
                { error: "invalid_grant", error_description: "Invalid PIN" },
                { status: 400 },
            );
        }

        // 4. Generate access token and c_nonce
        const accessToken = uuidv4();
        const cNonce = uuidv4();
        const cNonceExpiresAt = new Date(Date.now() + 5 * 60 * 1000); // 5 minutes

        // 5. Create a new issuance session
        const userData = (global as any).userDataStore?.get(code);
        await createIssuanceSession(
            uuidv4(),
            authCode.id,
            accessToken,
            cNonce,
            cNonceExpiresAt,
            userData,
        );

        // 6. Mark the code as used and clean up temporary data
        await markAuthorizationCodeAsUsed(code);
        (global as any).txCodeStore?.delete(code);
        (global as any).userDataStore?.delete(code);

        // 7. Return the access token response
        return NextResponse.json({
            access_token: accessToken,
            token_type: "Bearer",
            expires_in: 3600, // 1 hour
            c_nonce: cNonce,
            c_nonce_expires_in: 300, // 5 minutes
        });
    } catch (error) {
        console.error("Token endpoint error:", error);
        return NextResponse.json({ error: "server_error" }, { status: 500 });
    }
}
```

इस एंडपॉइंट में मुख्य चरण:

1. **अनुदान प्रकार मान्य करें:** यह सुनिश्चित करता है कि वॉलेट सही `pre-authorized_code`
   अनुदान प्रकार का उपयोग कर रहा है।
2. **कोड मान्य करें:** यह जांचता है कि `pre-authorized_code` डेटाबेस में मौजूद है, समाप्त
   नहीं हुआ है, और पहले उपयोग नहीं किया गया है।
3. **पिन मान्य करें:** यह वॉलेट से `user_pin` की तुलना हमारे द्वारा पहले संग्रहीत
   `tx_code` से करता है ताकि यह सुनिश्चित हो सके कि यूजर ने लेनदेन को अधिकृत किया है।
4. **टोकन जेनरेट करें:** यह एक सुरक्षित `access_token` और एक `c_nonce` (क्रेडेंशियल नॉन्स)
   बनाता है, जो क्रेडेंशियल एंडपॉइंट पर रीप्ले हमलों को रोकने के लिए एक वन-टाइम-यूज़ मान
   है।
5. **सत्र बनाएँ:** यह डेटाबेस में एक नया `issuance_sessions` रिकॉर्ड बनाता है, जो एक्सेस
   टोकन को यूजर के डेटा से जोड़ता है।
6. **कोड को प्रयुक्त के रूप में चिह्नित करें:** एक ही ऑफ़र को दो बार उपयोग करने से रोकने
   के लिए, यह `pre-authorized_code` को प्रयुक्त के रूप में चिह्नित करता है।
7. **टोकन लौटाएँ:** यह `access_token` और `c_nonce` को वॉलेट को लौटाता है।

#### 4.5.3 `/api/issue/credential`: हस्ताक्षरित क्रेडेंशियल जारी करें

यह अंतिम और सबसे महत्वपूर्ण एंडपॉइंट है। वॉलेट `/token` एंडपॉइंट से प्राप्त एक्सेस टोकन का
उपयोग इस रूट पर एक प्रमाणित `POST` अनुरोध करने के लिए करता है। इस एंडपॉइंट का काम अंतिम
सत्यापन करना, क्रिप्टोग्राफ़िक रूप से हस्ताक्षरित क्रेडेंशियल बनाना, और इसे वॉलेट को
लौटाना है।

निम्नलिखित सामग्री के साथ `src/app/api/issue/credential/route.ts` फ़ाइल बनाएं:

```typescript
// src/app/api/issue/credential/route.ts
import { NextRequest, NextResponse } from "next/server";
import { v4 as uuidv4 } from "uuid";
import {
    getIssuanceSessionByToken,
    updateIssuanceSession,
    createIssuedCredential,
    getActiveIssuerKey,
} from "@/lib/database";
import {
    createJWTVerifiableCredential,
    importIssuerKeyPair,
    generateIssuerDid,
} from "@/lib/crypto";

export async function POST(request: NextRequest) {
    try {
        // 1. Validate the Bearer token
        const authHeader = request.headers.get("authorization");
        const accessToken = authHeader?.substring(7);
        const session = await getIssuanceSessionByToken(accessToken);

        if (!session) {
            return NextResponse.json({ error: "invalid_token" }, { status: 401 });
        }

        // 2. Get the user data from the session
        const userData = session.user_data;
        if (!userData) {
            return NextResponse.json({ error: "missing_user_data" }, { status: 400 });
        }

        // 3. Get the active issuer key
        const issuerKey = await getActiveIssuerKey();
        if (!issuerKey) {
            // In a real application, you would have a more robust key management system.
            // For this demo, we can generate a key on the fly if one doesn't exist.
            // This part is omitted for brevity but is in the repository.
            return NextResponse.json(
                {
                    error: "server_error",
                    error_description: "Failed to get issuer key",
                },
                { status: 500 },
            );
        }

        // 4. Create the JWT-VC
        const issuerDid = generateIssuerDid();
        const keyPair = await importIssuerKeyPair(
            issuerKey.key_id,
            issuerKey.public_key,
            issuerKey.private_key,
            issuerDid,
        );
        const subjectId = `did:example:${uuidv4()}`;
        const credentialData = await createJWTVerifiableCredential(
            userData,
            keyPair,
            subjectId,
            process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000",
        );

        // 5. Store the issued credential in the database
        await createIssuedCredential(/* ... credential details ... */);
        await updateIssuanceSession(session.id, "credential_issued");

        // 6. Return the signed credential
        return NextResponse.json({
            format: "jwt_vc",
            credential: credentialData,
            c_nonce: uuidv4(), // A new nonce for subsequent requests
            c_nonce_expires_in: 300,
        });
    } catch (error) {
        console.error("Credential endpoint error:", error);
        return NextResponse.json({ error: "server_error" }, { status: 500 });
    }
}
```

इस एंडपॉइंट में मुख्य चरण:

1. **टोकन मान्य करें:** यह `Authorization` हेडर में एक मान्य `Bearer` टोकन की जांच करता है
   और इसका उपयोग सक्रिय जारी करने के सत्र को देखने के लिए करता है।
2. **यूजर डेटा प्राप्त करें:** यह यूजर के दावों के डेटा को पुनः प्राप्त करता है, जिसे सत्र
   में संग्रहीत किया गया था जब टोकन बनाया गया था।
3. **जारीकर्ता कुंजी लोड करें:** यह डेटाबेस से जारीकर्ता की सक्रिय हस्ताक्षर कुंजी को लोड
   करता है। एक वास्तविक दुनिया के परिदृश्य में, यह एक सुरक्षित कुंजी प्रबंधन प्रणाली
   द्वारा प्रबंधित किया जाएगा।
4. **क्रेडेंशियल बनाएँ:** यह JWT-VC का निर्माण और हस्ताक्षर करने के लिए
   `src/lib/crypto.ts` से हमारे `createJWTVerifiableCredential` हेल्पर को कॉल करता है।
5. **जारी करना लॉग करें:** यह ऑडिटिंग और निरस्तीकरण उद्देश्यों के लिए डेटाबेस में जारी किए
   गए क्रेडेंशियल का एक रिकॉर्ड सहेजता है।
6. **क्रेडेंशियल लौटाएँ:** यह एक JSON प्रतिक्रिया में हस्ताक्षरित क्रेडेंशियल को वॉलेट को
   लौटाता है। वॉलेट तब इसे सुरक्षित रूप से संग्रहीत करने के लिए जिम्मेदार होता है।

## 5. Issuer चलाना और अगले चरण

अब आपके पास एक डिजिटल क्रेडेंशियल जारीकर्ता का एक पूर्ण, एंड-टू-एंड कार्यान्वयन है। यहाँ
इसे स्थानीय रूप से कैसे चलाना है और इसे एक प्रूफ-ऑफ-कॉन्सेप्ट से उत्पादन-तैयार एप्लिकेशन
तक ले जाने के लिए आपको क्या विचार करने की आवश्यकता है।

### 5.1 उदाहरण कैसे चलाएं

1. **रिपॉजिटरी को क्लोन करें:**

    ```bash
    git clone https://github.com/corbado/digital-credentials-example.git
    cd digital-credentials-example
    ```

2. **निर्भरताएँ स्थापित करें:**

    ```bash
    npm install
    ```

3. **डेटाबेस शुरू करें:** सुनिश्चित करें कि Docker चल रहा है, फिर MySQL कंटेनर शुरू करें:

    ```bash
    docker-compose up -d
    ```

4. **पर्यावरण कॉन्फ़िगर करें और टनल चलाएं:** यह स्थानीय परीक्षण के लिए सबसे महत्वपूर्ण कदम
   है। चूंकि आपके मोबाइल वॉलेट को इंटरनेट पर आपके विकास मशीन से कनेक्ट करने की आवश्यकता
   है, इसलिए आपको अपने स्थानीय सर्वर को एक सार्वजनिक HTTPS URL के साथ उजागर करना होगा। हम
   इसके लिए `ngrok` का उपयोग करेंगे।

    a. **ngrok शुरू करें:**

    ```bash
    ngrok http 3000
    ```

    b. [ngrok](https://www.corbado.com/blog/multi-device-passkey-login-corbado-ngrok) आउटपुट से **HTTPS URL को
    कॉपी करें** (जैसे, `https://random-string.ngrok.io`)। c. एक `.env.local` फ़ाइल
    **बनाएं** और URL सेट करें:

    ```
    NEXT_PUBLIC_BASE_URL=https://<your-ngrok-url>
    ```

5. **एप्लिकेशन चलाएं:**

    ```bash
    npm run dev
    ```

    अपने ब्राउज़र को `http://localhost:3000/issue` पर खोलें। अब आप फ़ॉर्म भर सकते हैं, और
    जेनरेट किया गया QR कोड सही ढंग से आपके सार्वजनिक
    [ngrok](https://www.corbado.com/blog/multi-device-passkey-login-corbado-ngrok) URL को इंगित करेगा, जिससे आपका
    मोबाइल वॉलेट कनेक्ट हो सकेगा और क्रेडेंशियल प्राप्त कर सकेगा।

### 5.2 HTTPS और `ngrok` का महत्व

डिजिटल क्रेडेंशियल प्रोटोकॉल सुरक्षा को सर्वोच्च प्राथमिकता के रूप में बनाया गया है। इस
कारण से, वॉलेट लगभग हमेशा एक असुरक्षित (`http://`) कनेक्शन पर एक जारीकर्ता से कनेक्ट करने
से इनकार कर देंगे। पूरी प्रक्रिया एक सुरक्षित **HTTPS** कनेक्शन पर निर्भर करती है, जो एक
**SSL प्रमाणपत्र** द्वारा सक्षम है।

`ngrok` जैसी एक टनल सेवा दोनों समस्याओं को हल करती है, एक सुरक्षित, सार्वजनिक-सामना करने
वाला HTTPS URL (एक मान्य SSL प्रमाणपत्र के साथ) बनाकर जो सभी ट्रैफ़िक को आपके स्थानीय
विकास सर्वर पर अग्रेषित करता है। वॉलेट को HTTPS की आवश्यकता होती है और वे असुरक्षित
(`http://`) एंडपॉइंट्स से कनेक्ट करने से इनकार कर देंगे। यह किसी भी वेब सेवा का परीक्षण
करने के लिए एक आवश्यक उपकरण है जिसे मोबाइल उपकरणों या बाहरी वेबहुक के साथ इंटरैक्ट करने की
आवश्यकता होती है।

### 5.3 इस ट्यूटोरियल के दायरे से बाहर क्या है

यह उदाहरण जानबूझकर कोर जारी करने के प्रवाह पर केंद्रित है ताकि इसे समझना आसान हो सके।
निम्नलिखित विषय दायरे से बाहर माने जाते हैं:

- **उत्पादन-तैयार सुरक्षा:** जारीकर्ता शैक्षिक उद्देश्यों के लिए है। एक उत्पादन प्रणाली को
  डेटाबेस में कुंजी संग्रहीत करने के बजाय एक सुरक्षित कुंजी प्रबंधन प्रणाली (KMS), मजबूत
  त्रुटि हैंडलिंग, दर-सीमन और व्यापक ऑडिट लॉगिंग की आवश्यकता होगी।
- **क्रेडेंशियल निरस्तीकरण:** यह गाइड जारी किए गए क्रेडेंशियल को रद्द करने के लिए एक तंत्र
  लागू नहीं करता है। जबकि स्कीमा में भविष्य के उपयोग के लिए एक `revoked` ध्वज शामिल है,
  यहाँ कोई निरस्तीकरण तर्क प्रदान नहीं किया गया है।
- **ऑथराइजेशन कोड फ्लो:** हमने विशेष रूप से `pre-authorized_code` फ्लो पर ध्यान केंद्रित
  किया। `authorization_code` फ्लो का एक पूर्ण कार्यान्वयन एक यूजर सहमति स्क्रीन और अधिक
  जटिल [OAuth 2.0](https://www.corbado.com/glossary/oauth2) तर्क की आवश्यकता होगी।
- **यूजर प्रबंधन:** गाइड में जारीकर्ता के लिए कोई यूजर प्रमाणीकरण या प्रबंधन शामिल नहीं
  है। यह माना जाता है कि यूजर पहले से ही प्रमाणित है और क्रेडेंशियल प्राप्त करने के लिए
  अधिकृत है।

## 6. निष्कर्ष

बस इतना ही! कोड के कुछ पन्नों के साथ, अब हमारे पास एक पूर्ण, एंड-टू-एंड डिजिटल क्रेडेंशियल
जारीकर्ता है जो:

1. क्रेडेंशियल का अनुरोध करने के लिए एक उपयोगकर्ता-अनुकूल फ्रंटएंड प्रदान करता है।
2. पूर्ण OpenID4VCI `pre-authorized_code` फ्लो को लागू करता है।
3. वॉलेट इंटरऑपरेबिलिटी के लिए सभी आवश्यक डिस्कवरी एंडपॉइंट्स को उजागर करता है।
4. एक सुरक्षित, मानकों-अनुपालन JWT-वेरिफ़ाएबल क्रेडेंशियल उत्पन्न और हस्ताक्षरित करता है।

जबकि यह गाइड एक ठोस आधार प्रदान करता है, एक उत्पादन-तैयार जारीकर्ता को मजबूत कुंजी
प्रबंधन, इन-मेमोरी स्टोर के बजाय स्थायी भंडारण, क्रेडेंशियल निरस्तीकरण, और व्यापक सुरक्षा
सुदृढीकरण जैसी अतिरिक्त सुविधाओं की आवश्यकता होगी। वॉलेट संगतता भी भिन्न होती है; परीक्षण
के लिए Sphereon [Wallet](https://www.corbado.com/blog/digital-wallet-assurance) की सिफारिश की जाती है, लेकिन अन्य
वॉलेट यहां लागू किए गए पूर्व-अधिकृत प्रवाह का समर्थन नहीं कर सकते हैं। हालांकि, कोर
बिल्डिंग ब्लॉक्स और इंटरैक्शन फ्लो वही रहेंगे। इन पैटर्न का पालन करके, आप किसी भी प्रकार
के डिजिटल क्रेडेंशियल के लिए एक सुरक्षित और इंटरऑपरेबल जारीकर्ता बना सकते हैं।

## 7. संसाधन

यहाँ कुछ प्रमुख संसाधन, विनिर्देश और उपकरण दिए गए हैं जिनका इस ट्यूटोरियल में उपयोग या
संदर्भ दिया गया है:

- **प्रोजेक्ट रिपॉजिटरी:**
    - [GitHub पर पूर्ण स्रोत कोड](https://github.com/corbado/digital-credentials-example)

- **प्रमुख विनिर्देश:**
    - [OpenID for Verifiable Credential Issuance (OpenID4VCI)](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html):
      मुख्य जारी करने वाला प्रोटोकॉल।
    - [W3C Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/): VCs
      के लिए मूलभूत मानक।
    - [The `did:web` Method](https://w3c-ccg.github.io/did-method-web/): हमारे जारीकर्ता
      की सार्वजनिक कुंजी के लिए उपयोग की जाने वाली DID विधि।

- **उपकरण:**
    - [Sphereon Wallet](https://sphereon.com/wallet/): इस गाइड में उपयोग किया गया परीक्षण
      वॉलेट।
    - ngrok: आपके स्थानीय विकास परिवेश के लिए एक सुरक्षित सुरंग बनाने के लिए।

- **लाइब्रेरीज़:**
    - Next.js: फ्रंटएंड और बैकएंड बनाने के लिए React फ्रेमवर्क।
    - [jose](https://github.com/panva/jose): JSON वेब टोकन (JWTs) बनाने और हस्ताक्षर करने
      के लिए।
    - [mysql2](https://github.com/sidorares/node-mysql2): [Node.js](https://www.corbado.com/blog/nodejs-passkeys)
      के लिए MySQL क्लाइंट।
