Documentation API

KonnectID est un provider OAuth2 qui permet aux utilisateurs de contrôler finement quelles données ils partagent avec votre app. L'API est exposée via tRPC sous /api/trpc/*.

Base URL

https://konnectid.me

Enveloppe tRPC — toutes les réponses sont encapsulées par tRPC. Les corps de réponse montrés dans cette doc correspondent à result.data dans la réponse JSON réelle : { "result": { "data": { … } } }

Authentification

Flow OAuth2 Authorization Code + PKCE recommandé:

  1. Redirigez l'utilisateur vers /oauth/authorize
  2. L'utilisateur accepte (ou révoque) les scopes demandés
  3. Récupérez le code sur votre redirect_uri
  4. Échangez le code contre des tokens via /api/trpc/oauth2.token
  5. Appelez /api/trpc/oauth2.userInfo avec l'access token

1. Authorize

Redirigez le navigateur de l'utilisateur vers cette URL. L'utilisateur doit être connecté à KonnectID.

GET /oauth/authorize

Query params

ParamTypeRequisDescription
client_idstringouiClient ID de votre app
redirect_uristringouiDoit matcher une URI enregistrée
scopestringouiScopes séparés par espaces
statestringCSRFToken anti-CSRF retourné tel quel
code_challengestringPKCEBase64url SHA-256, max 128 chars
code_challenge_method"S256" | "plain"PKCES256 recommandé

Réponse

Après approbation par l'utilisateur, redirection vers votre redirect_uri :

https://yourapp.com/callback?code=abc...&state=xyz

Le code expire après 10 minutes.

2. Token Exchange

Échange le code contre access_token + refresh_token.

POST /api/trpc/oauth2.token

Body

{
  "grantType": "authorization_code",
  "code": "auth_code_returned_by_authorize",
  "clientId": "app_xxxxxxxxxxxxxxxxxxxxxx",
  "clientSecret": "secret_xxxxxxxxxxxxxxxxxxxxxx",
  "redirectUri": "https://yourapp.com/callback",
  "codeVerifier": "PKCE_verifier_max_128_chars"
}

Réponse

{
  "access_token": "at_...",
  "refresh_token": "rt_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "email profile"
}

Le client secret est hashé en DB. KonnectID le retourne une seule fois à la création/régénération via le dashboard dev.

3. UserInfo

Retourne les fields consentis par l'utilisateur.

GET /api/trpc/oauth2.userInfo?input={"accessToken":"..."}

Réponse

{
  "sub": 42,
  "email": "user@example.com",
  "first_name": "Marie",
  "ville": "Lyon",
  "google_email": "marie@gmail.com",
  "discord_connections": ["github", "spotify"]
}

sub (numeric user id KonnectID) toujours présent. Le reste dépend strictement des consents accordés.

4. Refresh Token

POST /api/trpc/oauth2.token
{
  "grantType": "refresh_token",
  "refreshToken": "rt_...",
  "clientId": "app_...",
  "clientSecret": "secret_..."
}

Nouvelle paire renvoyée. L'ancien refresh est révoqué. Refresh expire après 30 jours.

Scopes disponibles

Les scopes correspondent aux data_fields configurés par l'admin KonnectID. Chaque field a un basePrice qui détermine le revenu par accès.

Champs standard

ScopeLabelPrixCatégorie
Aucun scope publié.

Champs provider (OAuth chaîné)

Préfixés par le nom du provider, ex: google_email, discord_connections. Disponibles uniquement si l'utilisateur a lié le compte correspondant via /dashboard/trust.

Champs requis (login bloqué)

Préfixez un scope par required: pour le rendre obligatoire. L'utilisateur ne pourra pas désélectionner ce champ et devra le renseigner (saisie possible directement sur l'écran de consentement) avant d'autoriser. Sans cela, l'autorisation est refusée.

scope=required:email required:phone profile

Ici email et phone sont obligatoires, tandis que profile reste facultatif. Le préfixe est retiré côté serveur (les consentements sont tracés par field).

Webhooks

Si vous configurez une webhookUrl sur votre app, KonnectID notifie chaque première autorisation utilisateur.

Payload (POST)

{
  "event": "user.authorized",
  "appId": 12,
  "userId": 42,
  "scope": "email profile",
  "grantedAt": "2026-05-11T08:42:00.000Z"
}

SSRF guard côté serveur: refus de toute URL loopback / IPs privées (RFC 1918 / CGNAT) / link-local / metadata cloud. Use HTTPS public uniquement. redirect: 'manual' côté fetch.

Erreurs

Codes tRPC retournés via TRPCError:

CodeSens
BAD_REQUESTInput invalide / Zod schema rejet
UNAUTHORIZEDToken manquant ou expiré
FORBIDDENApp désactivée / scope révoqué / cross-user
NOT_FOUNDClient ID inconnu / consent introuvable
CONFLICTIdentité non sellable / collision
INTERNAL_SERVER_ERRORErreur serveur inattendue

PKCE

Recommandé pour tous les clients publics (SPA, mobile, etc.).

# 1. Générer le verifier (43-128 chars URL-safe)
verifier = base64url(random(32))

# 2. Calculer le challenge
challenge = base64url(sha256(verifier))

# 3. Envoyer dans authorize:
codeChallenge = challenge
codeChallengeMethod = "S256"

# 4. Au token exchange, envoyer le verifier:
codeVerifier = verifier

Consentements granulaires

Chaque scope est tracé individuellement dans consents: {userId, appId, fieldId | providerScope, granted, grantedAt, revokedAt}.

L'utilisateur peut révoquer un scope à tout moment depuis /dashboard/consents. Effet immédiat: tokens marqués isRevoked: true, userInfo ne retourne plus le field.

Côté dev: votre app peut révoquer un user via apps.disconnectUser({ appId, userId }) (vérifie ownership).