> ## 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.

# Vérification d'identité Okta et Auth0

> Déléguez la vérification d'identité à Didit depuis Okta Identity Engine ou Auth0 via OpenID Connect avec Pushed Authorization Requests.

Didit expose un serveur d'autorisation OpenID Connect conçu spécialement pour la vérification d'identité déléguée.
Il implémente le contrat attendu par le framework « bring your own identity verification vendor » (BYO IDV) d'Okta : les requêtes d'autorisation poussées (PAR, RFC 9126), le flux de code d'autorisation avec PKCE, et un token d'ID portant un objet `verified_claims` OpenID Connect.

Les mêmes endpoints fonctionnent pour toute partie de confiance prenant en charge ce profil, y compris les Actions post-login d'Auth0.

## Endpoints

Chaque endpoint se trouve sous l'URL de base de la passerelle de vérification d'identité.

| Environnement | URL de base |
| - | - |
| Production | `https://apx.didit.me/auth/idv` |
| Sandbox | `https://apx.didit.me/auth/idv` |

La production et la sandbox utilisent les mêmes endpoints.
L'application associée à vos identifiants client détermine l'environnement.
Utilisez des identifiants client distincts et un workflow appartenant à l'application correspondante pour chaque environnement.
Consultez [Sandbox et données de test](https://docs.didit.me/fr/integration/sandbox-testing.md) pour connaître les résultats de vérification simulés.

Le document de découverte situé sur `{base}/.well-known/openid-configuration` les répertorie tous :

| Objectif | Endpoint |
| - | - |
| Pushed Authorization Request | `POST {base}/par` |
| Autorisation | `GET {base}/authorize` |
| Token | `POST {base}/token` |
| JWKS | `GET {base}/jwks` |
| Découverte | `GET {base}/.well-known/openid-configuration` |

L'authentification du client utilise `client_secret_basic` ou `client_secret_post`.
Le flux de code d'autorisation requiert PKCE avec `code_challenge_method=S256`.
Les tokens d'ID sont signés avec RS256 ; vérifiez-les par rapport à l'endpoint JWKS.

## Flux

1. La partie de confiance pousse la requête d'autorisation vers `POST /par`, en incluant le challenge PKCE, `state`, `nonce`, son `redirect_uri` enregistré, les scopes `openid profile identity_assurance`, et les attributs dont elle souhaite la vérification.
   Didit renvoie un `request_uri` à usage unique valide pendant 60 secondes.
2. La partie de confiance redirige le navigateur de l'utilisateur vers `GET /authorize?request_uri=...`.
   Didit crée une session de vérification et redirige l'utilisateur vers le flux de vérification hébergé.
3. Lorsque l'utilisateur a terminé, Didit évalue la décision de vérification, compare l'identité extraite aux attributs transmis et renvoie l'utilisateur vers le `redirect_uri` de la partie de confiance avec un `code` à usage unique et le `state` d'origine.
4. La partie de confiance échange le code auprès de `POST /token` avec son `code_verifier` et reçoit un token d'ID signé.

## Demande d'attributs

Envoyez les attributs à comparer au sein du paramètre standard OpenID Connect `claims` de la requête PAR.
`given_name` et `family_name` sont tous deux pris en charge, et tous deux font systématiquement l'objet d'une comparaison approximative.
`verified_claims` est un tableau, conforme au profil BYO IDV d'Okta et à la réponse du token d'ID ci-dessous.

```json theme={null}
{
  "id_token": {
    "verified_claims": [
      {
        "verification": {
          "trust_framework": { "value": "IDV-DELEGATED" },
          "assurance_level": { "value": "VERIFIED" }
        },
        "claims": {
          "given_name": { "value": "Lucas", "fuzzy": true },
          "family_name": { "value": "Fraggioli", "fuzzy": true }
        }
      }
    ]
  }
}
```

## Le token d'ID

Le token d'ID contient un tableau `verified_claims`. `assurance_level` vaut `VERIFIED` uniquement lorsque la session de vérification a été approuvée **et** que chaque attribut demandé correspond au document vérifié.
Dans le cas contraire, sa valeur est `FAILED`.

```json theme={null}
{
  "iss": "https://apx.didit.me/auth/idv",
  "sub": "eea9c5ca-63e6-437b-8e94-131c33f5dce8",
  "aud": "<your client id>",
  "nonce": "<your nonce>",
  "verified_claims": [
    {
      "verification": {
        "trust_framework": "IDV-DELEGATED",
        "assurance_level": "VERIFIED",
        "time": "2026-07-09T11:59:33Z",
        "verification_process": "eea9c5ca-63e6-437b-8e94-131c33f5dce8"
      },
      "claims": {
        "given_name": { "value": "Lucas Agustin", "fuzzy": true },
        "family_name": { "value": "Fraggioli", "fuzzy": true }
      }
    }
  ]
}
```

`verification_process` correspond à l'identifiant de session Didit, ce qui vous permet de rechercher la vérification dans la console ou via l'API des sessions.

> [!NOTE]
>
> Ne considérez l'utilisateur comme vérifié que si `trust_framework` vaut `IDV-DELEGATED` **et** qu'`assurance_level` vaut `VERIFIED`.
> Toute autre combinaison signifie que la vérification n'a pas abouti.

## Configurer Okta

Demandez à Didit d'activer la vérification d'identité pour vos applications de production et sandbox, d'associer chacune à son workflow de vérification, et d'enregistrer l'URL de rappel exacte dans chaque tenant Okta.
Une application existante ou une clé API seule ne suffit pas à activer cette intégration.

Dans l'Okta Admin Console, ajoutez un fournisseur de vérification d'identité personnalisé et renseignez les valeurs ci-dessous.
Okta crée l'intégration en tant que fournisseur d'identité de type `IDV_STANDARD` avec le protocole `ID_PROOFING`, que vous référencez ensuite depuis une règle de politique de gestion des comptes.

| Champ | Valeur |
| - | - |
| Issuer | `https://apx.didit.me/auth/idv` |
| PAR request URL | `https://apx.didit.me/auth/idv/par` |
| Authorize URL | `https://apx.didit.me/auth/idv/authorize` |
| Token URL | `https://apx.didit.me/auth/idv/token` |
| JWKS URL | `https://apx.didit.me/auth/idv/jwks` |
| Scopes | `openid`, `profile`, `identity_assurance` |
| Client ID | Le client ID de l'application Didit correspondante |
| Client secret | La clé API principale (Primary API key) de cette application, disponible sous **Developers > API keys** dans la Console Didit |

Configurez la production et la sandbox comme des intégrations de vérification d'identité distinctes avec leurs identifiants respectifs.
Utilisez les endpoints ci-dessus pour les deux.
Le secret client est requis même si le flux utilise également PKCE.
Une clé API nommée créée séparément ne peut pas remplacer la clé API principale (Primary API key) de l'application pour cette intégration.

Transmettez votre URL de rappel à Didit afin qu'elle puisse être enregistrée dans votre client.
Okta la génère sous la forme `{yourOktaOrgUrl}/idp/identity-verification/callback`.

## Configurer Auth0

Auth0 ne dispose pas d'emplacement natif pour les fournisseurs d'IDV ; la vérification s'exécute donc à partir d'une Action post-login qui redirige l'utilisateur vers le même flux.
Enregistrez `DIDIT_IDV_BASE`, `DIDIT_CLIENT_ID` et `DIDIT_CLIENT_SECRET` en tant que secrets d'Action, et déclarez `https://{yourAuth0Domain}/continue` comme URI de redirection auprès de Didit.

> [!NOTE]
>
> Auth0 ajoute son propre paramètre `state` à la redirection `/authorize` afin de pouvoir reprendre la connexion. Didit reprend ce `state` et le renvoie à `/continue`, ce qui permet à Auth0 de finaliser la transaction : vous n'avez pas besoin de transmettre le state d'Auth0 dans le corps de la requête PAR (il n'est généré qu'au moment de la redirection).

```javascript theme={null}
const crypto = require("crypto");

// Correlate each login's PKCE verifier with its own transaction so a retried
// or overlapping login can't overwrite it (which would fail the token exchange).
const txKey = (event) =>
  event.transaction?.id || event.request?.query?.state || "default";

const basicAuth = (event) =>
  Buffer.from(
    `${event.secrets.DIDIT_CLIENT_ID}:${event.secrets.DIDIT_CLIENT_SECRET}`
  ).toString("base64");

exports.onExecutePostLogin = async (event, api) => {
  if (event.user.app_metadata?.idv_status === "verified") return;

  const verifier = crypto.randomBytes(48).toString("base64url");
  const challenge = crypto.createHash("sha256").update(verifier).digest("base64url");

  const pending = { ...event.user.app_metadata?.idv_pkce };
  pending[txKey(event)] = verifier;
  api.user.setAppMetadata("idv_pkce", pending);

  const params = {
    response_type: "code",
    client_id: event.secrets.DIDIT_CLIENT_ID,
    redirect_uri: `https://${event.request.hostname}/continue`,
    scope: "openid profile identity_assurance",
    code_challenge: challenge,
    code_challenge_method: "S256",
    login_hint: event.user.email ?? "",
  };

  // Ask Didit to match the scanned document against this user's known name, so an
  // approved session for a different person can't mark this account verified.
  if (event.user.given_name && event.user.family_name) {
    params.claims = JSON.stringify({
      id_token: {
        verified_claims: [
          {
            verification: { trust_framework: { value: "IDV-DELEGATED" } },
            claims: {
              given_name: { value: event.user.given_name, fuzzy: true },
              family_name: { value: event.user.family_name, fuzzy: true },
            },
          },
        ],
      },
    });
  }

  const res = await fetch(`${event.secrets.DIDIT_IDV_BASE}/par`, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      Authorization: `Basic ${basicAuth(event)}`,
    },
    body: new URLSearchParams(params).toString(),
  });
  if (!res.ok) return api.access.deny("Identity verification is unavailable.");

  const { request_uri } = await res.json();
  api.redirect.sendUserTo(`${event.secrets.DIDIT_IDV_BASE}/authorize`, {
    query: { request_uri, client_id: event.secrets.DIDIT_CLIENT_ID },
  });
};

exports.onContinuePostLogin = async (event, api) => {
  const code = event.request.query.code;
  const pending = { ...event.user.app_metadata?.idv_pkce };
  const key = txKey(event);
  const verifier = pending[key];

  // Consume the verifier so it can't be reused.
  if (key in pending) {
    delete pending[key];
    api.user.setAppMetadata("idv_pkce", pending);
  }
  if (!code || !verifier) return api.access.deny("Identity verification was not completed.");

  const res = await fetch(`${event.secrets.DIDIT_IDV_BASE}/token`, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      Authorization: `Basic ${basicAuth(event)}`,
    },
    body: new URLSearchParams({
      grant_type: "authorization_code",
      code,
      code_verifier: verifier,
      redirect_uri: `https://${event.request.hostname}/continue`,
    }).toString(),
  });
  if (!res.ok) return api.access.deny("Identity verification could not be confirmed.");

  const { id_token } = await res.json();
  // This token comes directly from Didit's token endpoint over TLS with client
  // authentication (the trusted back-channel), so decoding is sufficient here.
  // For defense-in-depth, verify the RS256 signature against the JWKS endpoint
  // (plus iss / aud / exp / nonce) before trusting it.
  const payload = JSON.parse(Buffer.from(id_token.split(".")[1], "base64url").toString());
  const verification = payload.verified_claims?.[0]?.verification ?? {};

  if (verification.trust_framework === "IDV-DELEGATED" && verification.assurance_level === "VERIFIED") {
    api.user.setAppMetadata("idv_status", "verified");
    api.user.setAppMetadata("idv_session", verification.verification_process ?? null);
  } else {
    api.access.deny("Identity verification failed.");
  }
};
```

> [!NOTE]
>
> Stockez le `code_verifier` PKCE par transaction de connexion (avec pour clé `event.transaction.id`), et non dans un champ `app_metadata` unique.
> Si un utilisateur réessaie ou effectue des connexions simultanées, un champ unique partagé sera écrasé et l'échange de token échouera avec `invalid_grant`.

## Erreurs

Les erreurs suivent la RFC 6749 et la RFC 9126.
Un échec d'authentification du client renvoie `401` avec `{"error": "invalid_client"}` ; un `request_uri` ou un code d'autorisation expiré ou rejoué renvoie `400` avec `invalid_request` ou `invalid_grant`.

Les codes d'autorisation, les valeurs `request_uri` et les sessions de vérification sont tous à usage unique.


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