> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# JWT सत्यापन और JWKS: Didit प्रमाणीकरण टोकन

> Didit प्रमाणीकरण टोकन: PKCE कोड एक्सचेंज, ES256 एक्सेस और ID टोकन क्लेम, प्रति एप्लिकेशन डिस्कवरी और JWKS, की रोटेशन और रीफ़्रेश टोकन रीयूज़ डिटेक्शन।

> [!WARNING]
>
> प्रमाणीकरण नया है। इस पेज की हर कॉल **अंतिम उपयोगकर्ता** के क्रेडेंशियल का उपयोग करती है, या किसी क्रेडेंशियल का उपयोग नहीं करती। अपनी सत्यापन API कुंजी या console token इन endpoints पर कभी न भेजें।

हर एप्लिकेशन अपना अलग OpenID Connect issuer होता है। issuer आपके एप्लिकेशन का साइन-इन origin है:

* लाइव एप्लिकेशन के लिए `https://<name>.auth.didit.me`,
* सैंडबॉक्स एप्लिकेशन के लिए `https://<name>.sandbox.auth.didit.me`,
* या आपका अपना कस्टम डोमेन, जब वह सक्रिय हो जाए।

लाइव और सैंडबॉक्स अलग issuer हैं, और उनकी कुंजियाँ भी अलग हैं। `DIDIT_AUTH_ISSUER` को अपने issuer पर सेट करें, बिना ट्रेलिंग स्लैश के, और `DIDIT_APPLICATION_ID` को अपनी एप्लिकेशन ID पर सेट करें। टोकन इसे `aud` के रूप में रखते हैं। आपकी publishable key में issuer एन्कोड होता है: `pk_live_` या `pk_test_` के बाद का भाग, बिना पैडिंग वाले base64url में issuer है।

> [!NOTE]
>
> जब आपका कस्टम डोमेन सक्रिय होगा, तो वह डिफ़ॉल्ट होस्ट की जगह issuer बन जाएगा। नए टोकन उसे `iss` में बताएँगे, discovery `jwks_uri` को उसी की ओर इंगित करेगा, और आपकी publishable key बदल जाएगी। डिफ़ॉल्ट होस्ट के तहत जारी किए गए access token तब तक `GET /session/` में विफल होंगे जब तक उन्हें रिफ़्रेश नहीं किया जाता। डोमेन सक्रिय करते समय `DIDIT_AUTH_ISSUER` अपडेट करें, या 5 मिनट के भीतर पुराने टोकन समाप्त होने तक दोनों issuer स्वीकार करें।

इस पेज के सर्वर endpoints (discovery, keys, tokens और session) [Authentication API reference](https://docs.didit.me/hi/authentication/api-reference/get-openid-configuration.md) में हैं, जो [openapi-authentication.json](https://docs.didit.me/openapi-authentication.json) से बना है। होस्टेड साइन-इन पेज उसमें शामिल नहीं है।

## Discovery और कुंजियाँ

| अनुरोध | परिणाम |
| - | - |
| `GET {issuer}/.well-known/openid-configuration` | `issuer`, `authorization_endpoint`, `token_endpoint`, `jwks_uri` और वह सब जो issuer सपोर्ट करता है। |
| `GET {issuer}/.well-known/jwks.json` | `{"keys": [...]}`, सार्वजनिक साइनिंग कुंजियाँ। |

issuer यह सपोर्ट करता है:

* रिस्पॉन्स टाइप `code` और रिस्पॉन्स मोड `query`।
* Grant types `authorization_code` और `refresh_token`।
* केवल PKCE method `S256`। क्लाइंट सार्वजनिक हैं: कोई client secret नहीं है, और `client_id` आपकी एप्लिकेशन ID है।
* साइनिंग एल्गोरिदम `ES256` (P-256 कुंजियाँ)।
* Scopes `openid`, `email`, `phone`, `profile` और `offline_access`।

कोई userinfo, introspection या end-session endpoint नहीं है, और `prompt=none` समर्थित नहीं है। सत्र पढ़ने और जाँचने के लिए [`GET /session/`](#सत्र-जाँचें) का उपयोग करें।

### कैशिंग और रोटेशन

दोनों दस्तावेज़ `Cache-Control: public, max-age=300` के साथ जवाब देते हैं, इसलिए आप और shared caches इन्हें 5 मिनट तक रख सकते हैं। ये प्रति IP पते पर प्रति मिनट 1,200 और प्रति घंटे 12,000 अनुरोधों की अनुमति देते हैं, जो sign-in endpoints के लिए लागू सीमाओं से कहीं ज़्यादा है, क्योंकि कई backend instances अक्सर एक ही पता साझा करते हैं।

* हर कुंजी का एक `kid` होता है; नई key IDs `k_` से शुरू होते हैं। हर टोकन अपनी कुंजी को `kid` हेडर में बताता है।
* कुंजियाँ हर 30 दिन में रोटेट होती हैं। Didit अगली कुंजी उसके साइनिंग शुरू करने से एक दिन पहले प्रकाशित करता है, और पिछली कुंजी को साइनिंग बंद करने के बाद एक दिन तक प्रकाशित रखता है। 5 मिनट के लिए कैश किया गया JWKS इसलिए रोटेशन के दौरान भी सत्यापन जारी रखता है।
* कुंजियों को `kid` से कैश करें। जब कोई टोकन ऐसा `kid` बताए जो आपके पास नहीं है, तो JWKS को फिर से लाएँ, लेकिन अधिकतम एक बार प्रति मिनट, और यदि कुंजी अब भी न मिले तो टोकन अस्वीकार करें।
* जब Didit किसी समझौता की गई कुंजी को रद्द करता है, तो वह उसे तुरंत JWKS से हटा देता है, और `GET /session/` उस कुंजी द्वारा साइन किए गए हर टोकन को अस्वीकार करता है। नई कुंजी तुरंत साइनिंग शुरू करती है, बिना एक दिन की पूर्व-प्रकाशन अवधि के। 5 मिनट तक पुराना कैश किया गया JWKS अब भी रद्द की गई कुंजी को स्वीकार करता है और नई कुंजी को नहीं जानता, इसलिए कैश ताज़ा होने तक नए टोकन ऑफ़लाइन सत्यापन में विफल हो सकते हैं।

## साइन-इन शुरू करें

होस्टेड पेज खोलें, जो `authorization_endpoint` है:

```text theme={null}
{issuer}/sign-in?redirect_uri=...&state=...&code_challenge=...&code_challenge_method=S256
```

| पैरामीटर | नियम |
| - | - |
| `redirect_uri` | आपके [पंजीकृत redirect URLs](https://docs.didit.me/hi/authentication/overview.md#रीडायरेक्ट-url) में से किसी एक से बिल्कुल मेल खाना चाहिए। |
| `state` | अधिकतम 512 अक्षर। callback पर इसकी जाँच करें। |
| `code_challenge` | आपके PKCE verifier का base64url SHA-256: ठीक 43 अक्षर। |
| `code_challenge_method` | `S256`। |
| `client_id` | वैकल्पिक। आपकी एप्लिकेशन ID के बराबर होना चाहिए। |
| `nonce` | वैकल्पिक। ID token में कॉपी होता है, ताकि आप इसे इस साइन-इन से बाँध सकें। |
| `scope`, `login_hint`, `ui_locales`, `max_age`, `prompt` | वैकल्पिक। `login_hint` केवल ईमेल फ़ील्ड को पहले से भरता है। |

[क्विकस्टार्ट](https://docs.didit.me/hi/authentication/quickstarts/javascript.md) यह अनुरोध आपके लिए बनाते हैं।

## कोड का आदान-प्रदान करें

callback को `code` और `state` मिलते हैं। `state` जाँचें, फिर 60 सेकंड के भीतर code का आदान-प्रदान करें। हर code केवल एक बार काम करता है। body JSON या form-encoded हो सकती है।

```bash theme={null}
curl --fail-with-body "${DIDIT_AUTH_ISSUER}/token/" \
  -H 'Content-Type: application/json' \
  -d '{
    "grant_type": "authorization_code",
    "code": "CODE_FROM_CALLBACK",
    "code_verifier": "YOUR_PKCE_VERIFIER",
    "redirect_uri": "https://app.example.com/auth/callback"
  }'
```

```json theme={null}
{
  "access_token": "eyJhbGciOiJFUzI1NiIs...",
  "id_token": "eyJhbGciOiJFUzI1NiIs...",
  "refresh_token": "N4yZTl6y24YKYq77X_SO...",
  "token_type": "Bearer",
  "expires_in": 299,
  "session_id": "c9abf6cc-2d64-4bf2-b31e-a4d09a070ab1"
}
```

`expires_in` बताता है कि access token में कितने सेकंड बचे हैं। जब आप एकाधिक सत्रों का विकल्प बंद कर देते हैं, तो यह आदान-प्रदान व्यक्ति के बाकी सत्र समाप्त कर देता है।

| रिस्पॉन्स | अर्थ |
| - | - |
| `400 {"error": "invalid_grant"}` | code अज्ञात है, उपयोग हो चुका है या समय-सीमा समाप्त है; verifier या `redirect_uri` मेल नहीं खाता, या `redirect_uri` अब पंजीकृत नहीं है; व्यक्ति ब्लॉक किया गया है या उसका ईमेल अब सत्यापित नहीं है; या आपने वह साइन-इन विधि बंद कर दी है जिसका उन्होंने उपयोग किया। |
| `400 {"error": "invalid_client"}` | `client_id` आपकी एप्लिकेशन ID नहीं है। |
| `400 {"error": "mfa_required"}` | साइन-इन आपकी MFA नीति के अनुरूप नहीं था। |
| `400 {"<field>": [...]}` | कोई फ़ील्ड गायब या अमान्य है, उदाहरण के लिए `{"non_field_errors": ["invalid_request"]}` जब किसी grant में उसके आवश्यक फ़ील्ड न हों। |
| `400 {"<field>": "unknown_field"}` | body में ऐसा फ़ील्ड है जिसे endpoint स्वीकार नहीं करता, उदाहरण के लिए `client_secret`। सार्वजनिक क्लाइंट कोई नहीं भेजते। |

## एक्सेस टोकन के क्लेम

एक्सेस टोकन एक JWT है, जिसका हेडर `alg: ES256` और `typ: at+jwt` है। यह अधिकतम 5 मिनट तक मान्य रहता है, और कभी भी अपने सत्र से अधिक समय तक नहीं।

| क्लेम | अर्थ |
| - | - |
| `iss` | आपका जारीकर्ता। |
| `aud` | आपकी एप्लिकेशन ID। |
| `client_id` | आपकी एप्लिकेशन ID। |
| `sub` | इस एप्लिकेशन में व्यक्ति की पहचान ID। |
| `sid` | सत्र ID। किसी AI एजेंट के टोकन के लिए, वह एक्सेस ID जो व्यक्ति ने एजेंट को दी है। |
| `jti`, `iat`, `exp` | टोकन ID, जारी होने का समय और समाप्ति समय। |
| `vendor_data` | व्यक्ति का स्थिर संदर्भ, जो सत्यापन और लेन-देन निगरानी के साथ साझा किया जाता है। |
| `amr` | साइन-इन की विधि, जैसे `["passkey"]`। |
| `org` | व्यक्ति की आपके एप्लिकेशन में सबसे पुरानी संगठन सदस्यता का `{"id", "role"}`, या `null` जब उसकी कोई सदस्यता न हो या संगठन बंद हों। `GET /session/` सभी सदस्यताएँ सूचीबद्ध करता है। |
| `verified` | सत्यापन स्तर और आपके चुने हुए क्लेम, या `{}`। सत्र के दौरान स्वीकृत जाँच अगले रिफ़्रेश पर टोकन तक पहुँचती है। |
| `act` | व्यक्ति के लिए `null`। व्यक्ति की ओर से काम करने वाले AI एजेंट के लिए: `{"sub", "agent", "scopes"}`। देखें [चरण 5](#अपने-सर्वर-पर-टोकन-सत्यापित-करें)। |

ID टोकन का हेडर `typ: JWT` है। इसमें वही क्लेम होते हैं, सिवाय `client_id` के, साथ में `auth_time` और `email_verified`, और `email`, `name` तथा `phone_number` तब जब उनका कोई मान हो। केवल कोड एक्सचेंज से मिले ID टोकन में `nonce` होता है, जब साइन-इन में वह था। उसकी जाँच वहीं करें, क्योंकि रिफ़्रेश से मिले ID टोकन में वह कभी नहीं होता। ID टोकन को कभी एक्सेस टोकन के रूप में स्वीकार नहीं किया जाता।

## अपने सर्वर पर टोकन सत्यापित करें

किसी क्लेम पर भरोसा करने से पहले इन सबकी जाँच करें:

1. `alg` `ES256` हो और `typ` `at+jwt` हो। `none` सहित हर दूसरे एल्गोरिदम को अस्वीकार करें।
2. `kid` आपके एप्लिकेशन के JWKS में हो, और हस्ताक्षर सत्यापित हो।
3. `iss` आपके कॉन्फ़िगर किए गए जारीकर्ता के बराबर हो, और `aud` आपकी एप्लिकेशन ID के बराबर हो।
4. `exp` और `iat` मान्य हों, जिसमें 60 सेकंड तक का क्लॉक अंतर स्वीकार्य है, और `sub`, `sid` और `jti` मौजूद हों।
5. `act` `null` हो। किसी AI एजेंट का एक्सेस टोकन उसी कुंजी से साइन होता है और वही `typ`, `iss` और `aud` रखता है; केवल `act` उसे अलग करता है। उसे अस्वीकार करें, जब तक endpoint एजेंटों को स्वीकार करता हो। तब भी केवल वही अनुमति दें जो `act.scopes` देता है। `GET /session/` किसी एजेंट के टोकन को हमेशा अस्वीकार करता है।

जारीकर्ता हमेशा अपनी कॉन्फ़िगरेशन से लें, टोकन से कभी नहीं।

```javascript Node.js theme={null}
// npm install jose
import { createRemoteJWKSet, jwtVerify } from "jose";

const ISSUER = process.env.DIDIT_AUTH_ISSUER; // https://<name>.auth.didit.me
const AUDIENCE = process.env.DIDIT_APPLICATION_ID;
// Cache keys for 5 minutes; refetch an unknown kid at most once a minute.
const jwks = createRemoteJWKSet(new URL(`${ISSUER}/.well-known/jwks.json`), {
  cacheMaxAge: 5 * 60 * 1000,
  cooldownDuration: 60 * 1000,
});

export async function verifyAccessToken(token) {
  const { payload } = await jwtVerify(token, jwks, {
    issuer: ISSUER,
    audience: AUDIENCE,
    algorithms: ["ES256"],
    typ: "at+jwt",
    clockTolerance: 60,
    maxTokenAge: "5m", // also rejects an iat in the future
    requiredClaims: ["sub", "sid", "jti", "exp", "iat"],
  });
  if (payload.act !== null) throw new Error("agent tokens are not accepted");
  return payload; // payload.vendor_data is the person across Didit products
}
```

```python Python theme={null}
# pip install "pyjwt[crypto]"
import os
import threading
import time

import jwt

ISSUER = os.environ["DIDIT_AUTH_ISSUER"]  # https://<name>.auth.didit.me
AUDIENCE = os.environ["DIDIT_APPLICATION_ID"]
jwks = jwt.PyJWKClient(f"{ISSUER}/.well-known/jwks.json", lifespan=300)
refresh_lock = threading.Lock()
last_refresh = 0.0


def signing_key(kid: str | None) -> jwt.PyJWK:
    # PyJWKClient refetches for every unknown kid: limit it to once a minute.
    global last_refresh
    keys = {key.key_id: key for key in jwks.get_signing_keys()}
    if kid not in keys:
        with refresh_lock:
            if time.monotonic() - last_refresh >= 60:
                last_refresh = time.monotonic()
                fresh = jwks.get_signing_keys(refresh=True)
                keys = {key.key_id: key for key in fresh}
    if kid not in keys:
        raise jwt.InvalidTokenError("unknown signing key")
    return keys[kid]


def verify_access_token(token: str) -> dict:
    header = jwt.get_unverified_header(token)
    if header.get("typ") != "at+jwt":
        raise jwt.InvalidTokenError("not an access token")
    claims = jwt.decode(
        token,
        signing_key(header.get("kid")).key,
        algorithms=["ES256"],
        audience=AUDIENCE,
        issuer=ISSUER,
        leeway=60,
        options={"require": ["iss", "aud", "sub", "sid", "jti", "exp", "iat"]},
    )
    if "act" not in claims or claims["act"] is not None:
        raise jwt.InvalidTokenError("agent tokens are not accepted")
    return claims
```

ऑफ़लाइन सत्यापन उस सत्र को नहीं देख सकता जो अभी-अभी समाप्त हुआ हो। जब यह मायने रखता है, जैसे भुगतान से पहले, तो `GET /session/` को कॉल करें।

## सत्र जाँचें

```bash theme={null}
curl --fail-with-body "${DIDIT_AUTH_ISSUER}/session/" \
  -H "Authorization: Bearer $DIDIT_ACCESS_TOKEN"
```

प्रतिक्रिया में `sub`, `sid`, `application_id`, `vendor_data`, `amr`, `mfa_verified`, `email`, `email_verified`, `phone`, `verified_identity`, `verified`, `expires_at`, `post_logout_redirect_uri` और `organizations` होते हैं। हर संगठन प्रविष्टि में `organization_id`, `organization__name`, `role` और `custom_role` होते हैं।

यह जाँच ऑफ़लाइन सत्यापन से अधिक सख़्त है। यह तब भी विफल होती है जब सत्र रद्द हो चुका हो, निष्क्रिय हो गया हो या अपनी अवधि पार कर चुका हो। यह तब भी विफल होती है जब व्यक्ति अवरुद्ध हो या उसका ईमेल अब सत्यापित न हो। यह तब भी विफल होती है जब सत्र आपकी MFA नीति को अब पूरा नहीं करता (उदाहरण के लिए, व्यक्ति द्वारा ऑथेंटिकेटर ऐप जोड़ने या संगठन एडमिन बनने के बाद)। और यह तब भी विफल होती है जब टोकन किसी रिफ़्रेश से बदल दिया गया हो। विफलता पर `401 {"detail": "invalid_token"}` और `WWW-Authenticate: Bearer` लौटता है।

`GET /session/` प्रति IP पता प्रति मिनट 30 और प्रति घंटे 300 अनुरोधों की अनुमति देता है। यह सीमा `DELETE /session/` के साथ साझा है। हर अनुरोध पर टोकन ऑफ़लाइन सत्यापित करें, और यह कॉल उन क्षणों के लिए रखें जिनमें इसकी ज़रूरत हो। हर सफल कॉल को गतिविधि के रूप में गिना जाता है, इसलिए यह सत्र के `idle_timeout` को फिर से शुरू कर देती है।

सत्यापित ईमेल पहचान सत्यापन नहीं है। उसके लिए `verified` पढ़ें: यहाँ वह कोई न होने पर `null` होता है, जबकि टोकन में `{}` होता है।

## रिफ़्रेश टोकन

```bash theme={null}
curl --fail-with-body "${DIDIT_AUTH_ISSUER}/token/" \
  -H 'Content-Type: application/json' \
  -d '{"grant_type": "refresh_token", "refresh_token": "YOUR_REFRESH_TOKEN"}'
```

रिफ़्रेश एक नया एक्सेस टोकन, ID टोकन और रिफ़्रेश टोकन लौटाता है, और आपने जो रिफ़्रेश टोकन भेजा था वह काम करना बंद कर देता है।

* रिफ़्रेश टोकन एक रैंडम स्ट्रिंग है, जो सत्र समाप्त होने तक मान्य रहता है।
* **पुनः उपयोग सत्र समाप्त कर देता है।** जब कोई पहले से उपयोग किया जा चुका रिफ़्रेश टोकन दोबारा आता है, तो Didit इसे चोरी का संकेत मानता है। वह सत्र रद्द कर देता है और `400 {"error": "invalid_grant"}` लौटाता है। सत्र के टोकन भी काम करना बंद कर देते हैं, जिनमें पिछले रिफ़्रेश से मिले टोकन भी शामिल हैं। उनका रिफ़्रेश टोकन विफल होता है और उनका एक्सेस टोकन तुरंत `GET /session/` में विफल होता है। हालाँकि वह समाप्त होने तक, यानी 5 मिनट के भीतर, ऑफ़लाइन सत्यापित होता रहता है।
* किसी सत्र के लिए एक समय में केवल एक रिफ़्रेश चलाएँ, चाहे टैब, डिवाइस या सर्वर इंस्टेंस कोई भी हों, और तीनों टोकन एक साथ बदलें।
* यदि रिफ़्रेश `400` के साथ विफल हो या उसका कोई उत्तर न आए, तो साइन-इन फिर से शुरू करें। उसे बिना सोचे दोबारा न चलाएँ, क्योंकि जो रिफ़्रेश पहले से सफल हो चुका है उसके बाद का पुनः प्रयास पुनः उपयोग माना जाएगा।
* `503 {"error": "temporarily_unavailable"}` का अर्थ है कि रिफ़्रेश नहीं हुआ और आपका रिफ़्रेश टोकन अब भी काम करता है। बैकऑफ़ के साथ पुनः प्रयास करें।
* Didit रिफ़्रेश चलने से पहले `429` लौटाता है, इसलिए `Retry-After` के बाद पुनः प्रयास करें। टोकन endpoint प्रति IP पता प्रति मिनट 30 और प्रति घंटे 300 अनुरोधों की अनुमति देता है। इसके अलावा, आपके एप्लिकेशन के हर `POST` और `DELETE` साझा बॉट सुरक्षा बकेट का उपयोग करते हैं: इन endpoints पर प्रति IP पता प्रति मिनट 60 अनुरोध, और प्रति डिवाइस प्रति मिनट 30। पूरे एप्लिकेशन के लिए, रिफ़्रेश का बकेट प्रति मिनट 6,000 का है, और बाकी हर `POST` और `DELETE` (ब्राउज़र और साइन-इन चरण सहित) का बकेट प्रति मिनट 3,000 का है, ताकि रिफ़्रेश आपके साइन-इन को कभी खत्म न करें।
* 5 मिनट के एक्सेस टोकन के साथ, प्रति घंटे 300 रिफ़्रेश लगभग 25 सक्रिय लोगों के लिए पर्याप्त हैं। यदि आपका सर्वर एक ही पते से कई लोगों के लिए रिफ़्रेश करता है, तो रिफ़्रेश केवल तभी करें जब किसी अनुरोध को नए टोकन की ज़रूरत हो, और जहाँ संभव हो वहाँ ब्राउज़र या ऐप में रिफ़्रेश करें।

## साइन आउट

`DELETE /session/` को व्यक्ति के मौजूदा access token के साथ भेजने पर सत्र समाप्त हो जाता है और `204` लौटता है। उस सत्र का refresh token काम करना बंद कर देता है, और उसके access token तुरंत `GET /session/` में विफल होने लगते हैं। जिन टोकन की जाँच आप ऑफ़लाइन करते हैं, वे अपनी समाप्ति तक मान्य रहते हैं। इसके बाद, यदि आपने `post_logout_redirect_uri` सेट किया है, तो व्यक्ति को उस पर भेजें।

साइन आउट के लिए ऐसा access token चाहिए जिसे `GET /session/` स्वीकार करे: यानी जो समाप्त न हुआ हो और refresh द्वारा बदला न गया हो। `401` का अर्थ है कि सत्र समाप्त नहीं हुआ। refresh करें, फिर नए access token से साइन आउट करें। यदि refresh `400` के साथ विफल हो, तो वह सत्र अब उपयोग नहीं किया जा सकता।

सत्र इन स्थितियों में भी समाप्त हो जाता है:

* जब वह `session_lifetime` (डिफ़ॉल्ट 30 दिन) या `idle_timeout` (डिफ़ॉल्ट 1 दिन) पार कर जाए,
* जब एकाधिक सत्रों की अनुमति न होने पर व्यक्ति फिर से साइन इन करे,
* जब `revoke_on_password_change` चालू होने पर व्यक्ति अपना पासवर्ड रीसेट करे,
* जब आपकी टीम कंसोल में सत्र रद्द करे या व्यक्ति को ब्लॉक करे,
* जब उपयोग किया जा चुका refresh token दोबारा भेजा जाए।

## जीवनकाल

| आइटम | जीवनकाल |
| - | - |
| साइन-इन लेन-देन (`POST /authorize/`) | 10 मिनट |
| Authorization code | 60 सेकंड, केवल एक बार उपयोग |
| Access token | अधिकतम 5 मिनट |
| ID token | उसके साथ जारी किए गए access token जितना ही `exp` |
| Refresh token | सत्र समाप्त होने तक, केवल एक बार उपयोग |
| सत्र | `session_lifetime` और `idle_timeout` |
| Step-up | 30 मिनट |
| Discovery और JWKS cache | 5 मिनट |

## उद्देश्य के अनुसार क्रेडेंशियल संग्रहीत करें

* सत्यापन API कुंजियाँ और कंसोल टोकन अपने सर्वर पर रखें।
* ब्राउज़र के access token जहाँ संभव हो मेमोरी में रखें। सर्वर-रेंडर्ड ऐप्स के लिए सुरक्षित HttpOnly cookies का उपयोग करें, और cookie से प्रमाणित अनुरोधों को CSRF से बचाएँ।
* मोबाइल के refresh token प्लेटफ़ॉर्म-संरक्षित स्टोरेज में रखें। साइन आउट के बाद स्थानीय स्थिति साफ़ करें।
* टोकन कभी URL या लॉग में न डालें।

आगे बढ़ने के लिए [JavaScript quickstart](https://docs.didit.me/hi/authentication/quickstarts/javascript.md), [Next.js quickstart](https://docs.didit.me/hi/authentication/quickstarts/nextjs.md) या [Authentication API reference](https://docs.didit.me/hi/authentication/api-reference/get-openid-configuration.md) देखें।


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.