---
updatedAt: 2026-07-31T13:08:10.000Z
agentTools:
  projectIndex: https://developers.graphcomment.com/llms.txt
---

# Connecter (et créer si besoin) un utilisateur SSO

Connecte un utilisateur identifié par votre système, en le créant chez
GraphComment au premier appel. Le site est identifié par
`key.pubKey` ; les données utilisateur voyagent **dans le payload
signé** `key.auth`, pas dans le reste du corps.

**Payload JSON du jeton `key.auth` :**

| Champ | Requis | Rôle |
|---|---|---|
| `id` | oui | Identifiant de l'utilisateur **dans votre système** (uid externe). |
| `email` | oui | E-mail de l'utilisateur. |
| `username` | oui* | Pseudo. *Optionnel uniquement si l'option « pseudos gérés par GraphComment » est activée sur le site. |
| `language` | non | Code langue à 2 caractères (sinon langue du site, puis `en`). |
| `picture` | non | URL d'avatar. |

Fenêtre de validité du jeton sur ce transport : 5 minutes.
(Aucun en-tête d'authentification : la signature voyage **dans le
corps** de la requête, d'où l'absence de cadenas sur cette opération.)


# OpenAPI definition

````json
{
  "openapi": "3.0.3",
  "info": {
    "title": "GraphComment Public API",
    "version": "1.2.1",
    "license": {
      "name": "Proprietary — © Semiologic",
      "url": "https://graphcomment.com/"
    },
    "description": "Référence publique de l'API GraphComment pour les intégrateurs partenaires.\n\n## Identifiants de site — deux identifiants distincts\n- **Shortname** (`pubkey` / `website_public_id`) : l'identifiant unique et\n  public du site, visible dans le back-office GraphComment — « shortname »\n  est le terme employé dans le back-office et les guides. C'est lui\n  qu'utilisent les routes SSO, l'export, l'import partenaire et le\n  compteur public.\n- **Identifiant interne** (`website_id`) : identifiant technique\n  (24 caractères hexadécimaux), utilisé uniquement par les routes de\n  configuration des webhooks.\n\nLes deux ne sont **pas** interchangeables : utiliser l'un à la place de\nl'autre produit des erreurs 404/403 silencieuses.\n\n## Jeton SSO signé (`ssoData`)\nLes routes SSO et d'export sont authentifiées par un jeton signé généré\npar **votre serveur** avec la clé privée SSO du site :\n\n```\nssoData = base64(JSON(payload)) + \" \" + signature + \" \" + timestamp\n```\n\n- `payload` : objet JSON propre à chaque opération (documenté sur chacune) ;\n- `timestamp` : epoch Unix en **secondes** au moment de la génération ;\n- `signature` : HMAC-SHA1 **hexadécimal** de la chaîne\n  `base64(JSON(payload)) + \" \" + timestamp`, calculé avec la clé privée\n  SSO du site.\n\nLa signature est horodatée : fenêtre de validité de 3 à 5 minutes selon le\ntransport — générez le jeton au moment de l'appel. La clé privée SSO ne\ndoit **jamais** être exposée côté navigateur, et ces appels sont réservés\nau serveur-à-serveur.\n\n## Erreurs\nLes erreurs d'authentification renvoient en général `401` avec un corps\n`{ \"gcCode\": 410, \"msg\": \"...\" }`, et une signature invalide renvoie\n`403`. Exception notable : les routes de configuration des **webhooks**\nrépondent `200` avec un corps `{ \"gcCode\": 410, ... }` en cas d'appel non\nauthentifié — vérifiez toujours l'absence du champ `gcCode`. Les autres\nerreurs renvoient selon les routes un objet JSON (souvent\n`{ \"err\": \"...\" }`) ou un message texte brut — le format exact est\ndocumenté opération par opération.\n"
  },
  "servers": [
    {
      "url": "https://api.graphcomment.com/api"
    }
  ],
  "tags": [
    {
      "name": "SSO",
      "description": "Authentification déléguée serveur-à-serveur : votre backend signe les requêtes avec la clé privée SSO du site (jamais exposée côté navigateur). Un client PHP prêt à l'emploi enveloppe ces routes (signature comprise) : [graphcomment/sdk-api-php](https://github.com/graphcomment/sdk-api-php) (v3, MIT)."
    }
  ],
  "components": {
    "schemas": {
      "AuthError": {
        "type": "object",
        "description": "Erreur d'authentification (shortname inconnu, jeton périmé ou absent).",
        "properties": {
          "gcCode": {
            "type": "integer",
            "example": 410
          },
          "msg": {
            "type": "string",
            "example": "route need authentication - route expired"
          }
        }
      },
      "SimpleError": {
        "type": "object",
        "properties": {
          "err": {
            "type": "string"
          }
        }
      },
      "TokenPair": {
        "type": "object",
        "description": "Jetons de session GraphComment de l'utilisateur.",
        "properties": {
          "token": {
            "type": "string",
            "description": "JWT de session."
          },
          "refreshToken": {
            "type": "string",
            "description": "Jeton de renouvellement."
          }
        }
      }
    }
  },
  "paths": {
    "/users/sso/loginAndOrRegisterByUid": {
      "post": {
        "tags": [
          "SSO"
        ],
        "operationId": "ssoLoginAndOrRegisterByUid",
        "summary": "Connecter (et créer si besoin) un utilisateur SSO",
        "description": "Connecte un utilisateur identifié par votre système, en le créant chez\nGraphComment au premier appel. Le site est identifié par\n`key.pubKey` ; les données utilisateur voyagent **dans le payload\nsigné** `key.auth`, pas dans le reste du corps.\n\n**Payload JSON du jeton `key.auth` :**\n\n| Champ | Requis | Rôle |\n|---|---|---|\n| `id` | oui | Identifiant de l'utilisateur **dans votre système** (uid externe). |\n| `email` | oui | E-mail de l'utilisateur. |\n| `username` | oui* | Pseudo. *Optionnel uniquement si l'option « pseudos gérés par GraphComment » est activée sur le site. |\n| `language` | non | Code langue à 2 caractères (sinon langue du site, puis `en`). |\n| `picture` | non | URL d'avatar. |\n\nFenêtre de validité du jeton sur ce transport : 5 minutes.\n(Aucun en-tête d'authentification : la signature voyage **dans le\ncorps** de la requête, d'où l'absence de cadenas sur cette opération.)\n",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "key"
                ],
                "properties": {
                  "key": {
                    "type": "object",
                    "required": [
                      "pubKey",
                      "auth"
                    ],
                    "properties": {
                      "pubKey": {
                        "type": "string",
                        "description": "Shortname du site."
                      },
                      "auth": {
                        "type": "string",
                        "description": "Jeton `ssoData` signé (format décrit dans l'introduction)."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "language",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 2
            },
            "description": "Langue de repli (2 caractères) si absente du payload signé."
          }
        ],
        "responses": {
          "200": {
            "description": "Utilisateur connecté (créé si nécessaire).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenPair"
                }
              }
            }
          },
          "400": {
            "description": "Utilisateur ou profil introuvable (variante sans pseudo), ou requête invalide.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SimpleError"
                }
              }
            }
          },
          "401": {
            "description": "Shortname inconnu, jeton périmé ou `key.pubKey` absent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                }
              }
            }
          },
          "403": {
            "description": "Signature invalide (`{\"gcCode\": 410, \"msg\": \"sso data you provided is invalid...\"}`), ou `key.auth` absent (`{\"err\": \"error key\"}`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                }
              }
            }
          },
          "409": {
            "description": "Pseudo déjà utilisé.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "err": {
                      "type": "string",
                      "example": "pseudo already used, please change your pseudo"
                    },
                    "username": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Champ requis manquant dans le payload signé (`id`, `email` ou `username`) ou erreur interne.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SimpleError"
                }
              }
            }
          }
        }
      }
    }
  }
}
````