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

# Clés de signature (JWKS) | API d'authentification Didit

> GET /.well-known/jwks.json renvoie les clés publiques ES256 de Didit. Mise en cache par kid ; rotation tous les 30 jours, chevauchement d'un jour.

> [!WARNING]
>
> L'authentification est nouvelle. Ces endpoints sont servis par l'émetteur de votre application, tel que `https://<name>.auth.didit.me`, et n'acceptent jamais votre clé API Didit.

Les clés sont renouvelées tous les 30 jours, avec un jour de chevauchement de chaque côté, si bien qu'un JWKS mis en cache pendant 5 minutes permet de continuer à vérifier les tokens lors d'une rotation. Récupérez-le à nouveau lorsqu'un token mentionne un `kid` que vous ne possédez pas, au maximum une fois par minute. Voir [Mise en cache et rotation](https://docs.didit.me/fr/authentication/tokens-and-jwks.md#cache-et-rotation).


## OpenAPI

````yaml fr/openapi-authentication.json GET /.well-known/jwks.json
openapi: 3.0.3
info:
  title: Didit Authentication API
  version: 1.0.0
  description: >-
    Les endpoints côté serveur de Didit Authentication, le produit de connexion
    pour les utilisateurs finaux de votre application. Authentication est
    **nouveau** et arrive en production dans la version 2026-11-01.


    ## URL de base


    Chaque application constitue son propre émetteur OpenID Connect, et chaque
    endpoint ci-dessous est servi à la racine de cet émetteur :


    - `https://<name>.auth.didit.me` pour une application en production, ou
    `https://<name>.sandbox.auth.didit.me` pour une application en sandbox.

    - Votre propre domaine personnalisé, une fois actif. Il remplace alors
    l'hôte par défaut en tant qu'émetteur : les nouveaux tokens l'indiquent dans
    `iss`, la découverte pointe vers lui et la clé publiable change. Les tokens
    d'accès émis sous l'hôte par défaut échouent sur `/session/`, `/step-up/` et
    `/monitoring-token/` tant qu'ils ne sont pas rafraîchis.


    La production et le sandbox sont des applications distinctes ; ce sont donc
    des émetteurs distincts disposant de clés distinctes. La clé publiable est
    `pk_live_` (production) ou `pk_test_` (sandbox), suivie de l'encodage
    base64url sans remplissage de l'émetteur, sans barre oblique finale :
    décodez-la pour retrouver l'émetteur. Il s'agit d'un identifiant public, pas
    d'un identifiant d'authentification.


    Les mêmes endpoints JSON sont également servis sous
    `https://apx.didit.me/auth/v2/authentication/{application_id}/`. Les tokens
    indiquent toujours l'émetteur ci-dessus, quelle que soit l'adresse qui les a
    émis.


    ## Identifiants


    Ces endpoints n'acceptent jamais votre clé API Didit ni un token de console.


    - La découverte, le JWKS et `POST /token/` ne prennent aucun identifiant.
    Les clients sont publics : il n'y a pas de secret client, et un code
    d'autorisation est lié à son vérificateur PKCE (`S256`). `client_id` est
    l'identifiant de votre application.

    - `/session/`, `/step-up/` et `/monitoring-token/` prennent le token d'accès
    de la personne : `Authorization: Bearer <access_token>`. Chaque appel réussi
    compte comme une activité sur la session, ce qui réinitialise le délai
    d'inactivité de la session.


    ## Corps de requête


    Chaque endpoint acceptant un corps prend en charge les corps au format JSON,
    au format formulaire et multipart. Ce document liste le format JSON, ainsi
    que le format encodé en formulaire pour `POST /token/` tel que les clients
    OAuth l'envoient. Un champ que l'endpoint n'accepte pas renvoie `400
    {"<field>": "unknown_field"}`.


    ## Tokens


    - **Token d'accès** : un JWT ES256 avec l'en-tête `typ: at+jwt`, valide
    pendant au maximum 5 minutes et jamais plus longtemps que sa session.
    Revendications : `AccessTokenClaims`.

    - **Token d'identification** : un JWT ES256 avec l'en-tête `typ: JWT`.
    Revendications : `IDTokenClaims`. Il n'est jamais accepté en tant que token
    d'accès.

    - **Token d'actualisation** : une chaîne aléatoire renouvelée à chaque
    utilisation. Envoyer à nouveau un token d'actualisation déjà utilisé révoque
    la session.


    Vérifiez un token à l'aide des clés provenant de `GET
    /.well-known/jwks.json` : l'algorithme doit être `ES256`, `typ` doit
    correspondre au type de token, `iss` doit être égal à l'émetteur configuré
    et `aud` à l'identifiant de votre application. La vérification hors ligne ne
    détecte une session révoquée qu'une fois son token d'accès expiré ; `GET
    /session/` constitue la vérification instantanée.


    ## Erreurs


    | Corps | Quand |

    | --- | --- |

    | `{"error": "<code>"}` | Erreurs de `POST /token/`, toutes avec le statut
    `400`, au format de corps de la [section 5.2 de la RFC
    6749](https://www.rfc-editor.org/rfc/rfc6749#section-5.2) : `invalid_grant`,
    `invalid_client` et le `mfa_required` de Didit. Erreurs de service :
    `temporarily_unavailable`, `monitoring_unavailable`, `monitoring_disabled`.
    |

    | `{"<field>": ["<message>"]}` ou `{"<field>": "<code>"}` | La validation
    d'un champ de la requête a échoué. `non_field_errors` indique une règle
    s'appliquant à plusieurs champs, et `unknown_field` un champ que l'endpoint
    n'accepte pas. |

    | `{"detail": "<message>"}` | `401` (`invalid_token`), `403`
    (`origin_not_allowed`), `404` (application inconnue ou step-up inconnu) et
    `429` (limite de débit). Également `400` pour un corps impossible à analyser
    (`JSON parse error - ...`), `405` pour une méthode que l'endpoint ne prend
    pas en charge et `415` pour un `Content-Type` non pris en charge. |


    Une réponse `401` comporte également `WWW-Authenticate: Bearer`, et une
    réponse `429` comporte `Retry-After` en secondes.


    Deux refus ne sont pas au format JSON. Une adresse qui n'est pas un émetteur
    est refusée avant d'atteindre ces endpoints, par exemple avec une erreur
    `404` vide pour un nom `*.auth.didit.me` non utilisé. Un `step_up_id` qui
    n'est pas un UUID renvoie une page `404`.


    ## Limites de débit


    Une requête doit franchir deux couches.


    **Limites par endpoint**, comptabilisées par adresse IP cliente, par
    application et par endpoint :


    - Découverte et JWKS : 1 200 requêtes par minute et 12 000 par heure.

    - Tous les autres endpoints : 30 requêtes par minute et 300 par heure. `GET`
    et `DELETE /session/` partagent le même décompte.


    En cas de dépassement de la limite : `429 {"detail": "Too many requests from
    this IP address. Please wait before trying again. Expected available in 60
    seconds."}`, ou `3600 seconds` pour la limite horaire.


    **Protection contre les bots**, sur chaque `POST` et `DELETE`. Ses
    compartiments sont partagés par toutes les requêtes `POST` et `DELETE` de
    l'application, y compris les étapes de connexion hébergées, et se
    remplissent en continu :


    - 60 requêtes par minute par adresse IP cliente.

    - 30 requêtes par minute par appareil, pour les requêtes qui identifient
    leur appareil, comme le font les SDK natifs et de navigateur.

    - 3 000 requêtes par minute pour l'ensemble de l'application, toutes
    adresses IP confondues. Les rafraîchissements de token (`POST /token/` avec
    `grant_type=refresh_token`) utilisent plutôt leur propre compartiment : 6
    000 par minute pour l'ensemble de l'application.


    En cas de dépassement d'un compartiment : `429 {"detail": "rate_limited
    Expected available in N seconds."}` (`1 second.` lorsque N vaut 1). Le
    compartiment par IP s'étend sur l'ensemble des endpoints ; ainsi, une
    adresse unique qui combine des appels de token, de step-up et de déconnexion
    peut le vider avant même d'atteindre la moindre limite par endpoint. Le
    compartiment applicatif prend en compte chaque client de l'application, y
    compris les navigateurs.


    Les deux couches répondent avant l'exécution de l'endpoint : une requête
    ayant reçu une réponse `429` n'a donc pas été exécutée. Réessayez-la après
    le délai indiqué par `Retry-After`, y compris pour un rafraîchissement.


    ## Requêtes de navigateur


    Une requête comportant un en-tête `Origin` doit provenir de l'une des
    origines propres à l'application ou de ses origines autorisées, sous peine
    de recevoir une erreur `403 origin_not_allowed`. Les requêtes de serveur à
    serveur n'envoient aucun en-tête `Origin`.


    ## Non traité ici


    Ces routes desservent les pages hébergées, les SDK natifs et de navigateur,
    ainsi que les agents IA. Elles ne font pas partie du contrat serveur, et les
    SDK serveur ne les appellent pas :


    - Étapes de connexion : `POST /authorize/`, `POST /password/`, `/email/*`,
    `/phone/*`, `/password-reset/*`, `/passkeys/*`, `/mfa/*`, `/social/*` et
    `/protection/complete/`.

    - Pages hébergées et rappels : `/sign-in`, `/magic`, `/invite`, `/reset`,
    `/verify/`, `/sso/*`, `/wallet/callback/`, `/logo/` et les fichiers
    d'association d'applications natives sous `/.well-known/`.

    - Accès pour les agents : `/agents/*` et `/grants/`, y compris `GET
    /agents/session/`, la vérification en direct du token d'accès d'un agent IA.
    L'accès des agents ne fait pas partie de cette version du contrat.

    - L'API de gestion de la console pour Authentication, qui requiert des
    sessions de console.


    Les événements d'Authentication ne sont pas encore transmis sous forme de
    webhooks ; ce document ne définit donc aucun webhook.
servers:
  - url: https://{issuer}
    description: 'L''émetteur de votre application : son origine de connexion.'
    variables:
      issuer:
        default: your-app.auth.didit.me
        description: >-
          L'hôte de l'émetteur : `<name>.auth.didit.me` (production),
          `<name>.sandbox.auth.didit.me` (sandbox) ou votre domaine personnalisé
          actif.
security: []
tags:
  - name: Issuer
    description: Clés de découverte et de signature, pour vérifier les tokens.
  - name: Tokens
    description: Les autorisations par code d'autorisation et de renouvellement.
  - name: Sessions
    description: Vérifier et terminer la session associée à un token d'accès.
  - name: Step-ups
    description: Vérification avant une action sensible.
  - name: Monitoring
    description: >-
      Informations d'authentification de l'appareil pour les événements de
      surveillance des transactions.
paths:
  /.well-known/jwks.json:
    get:
      tags:
        - Issuer
      summary: Obtenir les clés de signature
      description: >-
        Les clés publiques ES256 de l'application. Chaque token indique sa clé
        dans l'en-tête `kid`.


        Les clés changent par rotation tous les 30 jours. La clé suivante est
        publiée un jour avant de commencer à signer, et la clé précédente reste
        publiée pendant un jour après son arrêt, de sorte qu'un JWKS mis en
        cache jusqu'à 5 minutes continue de vérifier pendant une rotation.
        Mettez les clés en cache par `kid` et récupérez à nouveau le JWKS
        lorsqu'un token indique un `kid` que vous ne possédez pas, au maximum
        une fois par minute. Lorsque Didit révoque une clé compromise, la clé
        quitte ce document immédiatement et `GET /session/` rejette chaque token
        qu'elle a signé. Une clé de remplacement commence à signer
        immédiatement, sans le délai d'un jour de publication anticipée, de
        sorte qu'un JWKS mis en cache jusqu'à 5 minutes plus tôt accepte
        toujours la clé révoquée et ne connaît pas encore celle de remplacement.
      operationId: authentication_get_jwks
      responses:
        '200':
          description: Les clés publiées.
          headers:
            Cache-Control:
              description: >-
                `public, max-age=300` : vous et les caches partagés pouvez
                conserver une réponse réussie pendant 5 minutes, soit la durée
                de vie d'un token d'accès.
              schema:
                type: string
                example: public, max-age=300
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JSONWebKeySet'
              examples:
                Keys:
                  summary: Une clé de signature
                  value:
                    keys:
                      - kty: EC
                        crv: P-256
                        x: ZDN2unLMultuZwNY_ZKMvsKLRrlC_usPFZyK77cnDO8
                        'y': CfLKoaJNvhFpDWqG56H8dD_j0f1c93RdR63bhIdTh3I
                        kid: k_8sJ2dQvX0nLr5TfYb3HkPw
                        use: sig
                        alg: ES256
        '403':
          $ref: '#/components/responses/OriginNotAllowed'
        '404':
          $ref: '#/components/responses/ApplicationNotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/TemporarilyUnavailable'
      security:
        - {}
components:
  schemas:
    JSONWebKeySet:
      type: object
      description: >-
        Les clés de signature publiques publiées de l'application. Généralement
        une seule clé ; deux lors d'une rotation, lorsque la clé suivante est
        publiée un jour avant de signer et que la clé précédente reste un jour
        après son arrêt.
      required:
        - keys
      properties:
        keys:
          type: array
          items:
            $ref: '#/components/schemas/JSONWebKey'
    JSONWebKey:
      type: object
      description: Une clé publique P-256 au format JWK (RFC 7517).
      required:
        - kty
        - crv
        - x
        - 'y'
        - kid
        - use
        - alg
      properties:
        kty:
          type: string
          enum:
            - EC
        crv:
          type: string
          enum:
            - P-256
        x:
          type: string
          description: La coordonnée x du point public, base64url.
          example: ZDN2unLMultuZwNY_ZKMvsKLRrlC_usPFZyK77cnDO8
        'y':
          type: string
          description: La coordonnée y du point public, base64url.
          example: CfLKoaJNvhFpDWqG56H8dD_j0f1c93RdR63bhIdTh3I
        kid:
          type: string
          description: >-
            ID de clé, le `kid` dans l'en-tête de chaque token signé par cette
            clé. Les nouveaux ID de clé commencent par `k_`.
          example: k_8sJ2dQvX0nLr5TfYb3HkPw
        use:
          type: string
          enum:
            - sig
        alg:
          type: string
          enum:
            - ES256
    Detail:
      type: object
      required:
        - detail
      properties:
        detail:
          type: string
          example: invalid_token
    OAuthError:
      title: OAuth error
      type: object
      description: Une erreur OAuth (RFC 6749 section 5.2) ou une erreur de service.
      required:
        - error
      properties:
        error:
          type: string
          example: invalid_grant
  responses:
    OriginNotAllowed:
      description: >-
        La requête contenait un `Origin` qui n'est ni celui de l'application ni
        dans ses origines autorisées. Les requêtes serveur n'envoient pas de
        `Origin`.
      headers:
        Cache-Control:
          description: 'Toujours `no-store` : ne jamais mettre cette réponse en cache.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Toujours `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            OriginNotAllowed:
              summary: Origine non autorisée
              value:
                detail: origin_not_allowed
    ApplicationNotFound:
      description: >-
        L'adresse appartient à une application, mais Authentication n'est pas
        actif pour celle-ci ; sur l'adresse `apx.didit.me`, l'identifiant de
        l'application est inconnu. Une adresse n'appartenant à aucune
        application est refusée avant d'atteindre l'endpoint, et cette réponse
        n'est pas en JSON.
      headers:
        Cache-Control:
          description: 'Toujours `no-store` : ne jamais mettre cette réponse en cache.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Toujours `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            ApplicationNotFound:
              summary: Application inconnue
              value:
                detail: No Configuration matches the given query.
    RateLimited:
      description: >-
        Trop de requêtes issues de cette adresse IP pour cet endpoint, ou, sur
        un `POST` ou `DELETE`, un compartiment de protection contre les robots
        est vide. Rien n'a été exécuté : attendez `Retry-After` secondes, puis
        réessayez.
      headers:
        Retry-After:
          description: Secondes à attendre avant de réessayer.
          schema:
            type: integer
            example: 60
        Cache-Control:
          description: 'Toujours `no-store` : ne jamais mettre cette réponse en cache.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Toujours `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            RateLimited:
              summary: Limite du endpoint
              value:
                detail: >-
                  Too many requests from this IP address. Please wait before
                  trying again. Expected available in 60 seconds.
            BotProtection:
              summary: Limite de protection contre les bots (POST et DELETE)
              value:
                detail: rate_limited Expected available in 2 seconds.
    TemporarilyUnavailable:
      description: >-
        Un service dont dépend la requête est temporairement indisponible.
        Réessayez en augmentant progressivement l'intervalle entre les
        tentatives.
      headers:
        Cache-Control:
          description: 'Toujours `no-store` : ne jamais mettre cette réponse en cache.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Toujours `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/OAuthError'
          examples:
            TemporarilyUnavailable:
              summary: Réessayer plus tard
              value:
                error: temporarily_unavailable

````

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