Liens authentifiés
Cette documentation décrit comment intégrer votre application partenaire avec téo grâce aux liens authentifiés.
Vue d'ensemble
téo permet de créer des liens authentifiés vers votre application. Lorsqu'un utilisateur clique sur un lien partenaire dans téo, il est redirigé vers votre application avec un token JWT (JSON Web Token) signé cryptographiquement. Ce token contient les informations de contexte (dossier, utilisateur) et permet d'authentifier la requête.
Avantages
- Sécurité : Tokens signés avec algorithme ES256, impossibles à falsifier
- Contexte : Le token contient les informations du dossier et de l'utilisateur
- Expiration : Tokens à durée de vie limitée (5 minutes par défaut)
- Standard : Basé sur les standards JWT (RFC 7519) et JWKS
Enrichir les données via l'API
Le JWT contient les identifiants essentiels (UUID du dossier, ID utilisateur, etc.). Votre application peut utiliser ces identifiants pour appeler l'API téo et récupérer des informations complémentaires.
Le JWT partenaire sert uniquement à transmettre le contexte depuis téo. Pour appeler l'API téo, vous devez vous authentifier séparément avec vos identifiants OAuth2 (CLIENT_ID, CLIENT_SECRET) comme décrit dans la documentation d'authentification.
Par exemple, avec le dossierId contenu dans le JWT, vous pouvez :
- Vous authentifier à l'API avec vos identifiants OAuth2
- Appeler
GET /api/production/dossiers/{dossierId} - Récupérer les informations complètes : nom, prénom, email, historique des séances, documents...
Le JWT vous donne le contexte (quel dossier, quel utilisateur). L'API, avec vos propres identifiants, vous permet d'enrichir ce contexte avec toutes les données dont vous avez besoin.
Devenir partenaire
- Testez votre intégration avec l'environnement sandbox, accessible à tous sans demande préalable
- Demandez votre accès partenaire une fois vos tests validés (voir procédure)
Comment ça fonctionne
Une fois votre intégration configurée, un lien vers votre application apparaît directement dans les dossiers téo. Lorsqu'un utilisateur clique sur ce lien :
- téo génère automatiquement un token JWT signé contenant les informations du dossier
- L'utilisateur est redirigé vers votre application avec ce token
- Votre application vérifie le token et authentifie l'utilisateur
L'utilisateur accède ainsi à votre application dans le contexte du dossier qu'il consultait, sans avoir à se réauthentifier.
Flux d'authentification
Format du JWT
Structure
Le JWT est composé de trois parties séparées par des points : header.payload.signature
Header
{
"alg": "ES256",
"typ": "JWT",
"kid": "{key_id}"
}
| Champ | Description |
|---|---|
alg | Algorithme de signature (ES256) |
typ | Type de token (toujours JWT) |
kid | Identifiant de la clé utilisée pour la signature |
Payload (Claims)
| Claim | Type | Description |
|---|---|---|
iss | string | Émetteur du token (instance téo) |
sub | string | Sujet principal (UUID du dossier) |
dossierId | string | UUID du dossier |
dossierIid | integer | Identifiant interne (numérique) du dossier |
userId | string | UUID de l'utilisateur connecté |
userEmail | string | Email de l'utilisateur connecté |
slug | string | Identifiant de l'instance téo |
tenantId | string | Identifiant du tenant |
iat | integer | Timestamp de création (Unix) |
exp | integer | Timestamp d'expiration (Unix) |
Des claims supplémentaires peuvent être ajoutés selon les besoins de votre intégration. Contactez-nous pour en discuter.
Exemple de payload
{
"iss": "demo.teo-online.net",
"sub": "a2352f07-0439-499b-bb87-6e516d4e177c",
"dossierId": "a2352f07-0439-499b-bb87-6e516d4e177c",
"dossierIid": 12345,
"userId": "f011b0ab-5994-102b-b457-34c438cd5d6b",
"userEmail": "[email protected]",
"slug": "demo",
"tenantId": "f011b0ab-5994-102b-b457-34c438cd5d6b",
"iat": 1737820800,
"exp": 1737821100
}
Vérification du JWT
Endpoint JWKS
Les clés publiques pour vérifier les signatures sont disponibles via l'endpoint JWKS standard :
GET https://auth.teoapp.fr/.well-known/jwks.json
Les clés publiques changent rarement. Nous recommandons de les mettre en cache pendant 24 heures pour optimiser les performances.
Endpoint clé publique
Pour récupérer une clé spécifique par son identifiant :
GET https://auth.teoapp.fr/api/jwt/public-key?kid={kid}
Implémentation
Exemple PHP
<?php
use Firebase\JWT\JWT;
use Firebase\JWT\JWK;
// 1. Récupérer le token depuis l'URL
$token = $_GET['teo_jwt'] ?? null;
if (!$token) {
http_response_code(400);
die('Token manquant');
}
// 2. Récupérer les clés publiques (avec cache)
function getJwks(): array
{
$cacheFile = sys_get_temp_dir() . '/teo_jwks.json';
$cacheTime = 86400; // 24 heures
if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $cacheTime) {
return json_decode(file_get_contents($cacheFile), true);
}
$jwks = file_get_contents('https://auth.teoapp.fr/.well-known/jwks.json');
file_put_contents($cacheFile, $jwks);
return json_decode($jwks, true);
}
// 3. Vérifier et décoder le JWT
try {
$jwks = getJwks();
$keys = JWK::parseKeySet($jwks);
$decoded = JWT::decode($token, $keys);
// 4. Utiliser les données
$dossierId = $decoded->dossierId;
$dossierIid = $decoded->dossierIid;
$userEmail = $decoded->userEmail;
$slug = $decoded->slug;
// Votre logique métier...
} catch (Exception $e) {
http_response_code(401);
die('Token invalide: ' . $e->getMessage());
}
Dépendance requise :
composer require firebase/php-jwt
Exemple Node.js
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
// Configuration du client JWKS
const client = jwksClient({
jwksUri: 'https://auth.teoapp.fr/.well-known/jwks.json',
cache: true,
cacheMaxAge: 86400000 // 24 heures
});
// Fonction pour récupérer la clé
function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
if (err) return callback(err);
callback(null, key.getPublicKey());
});
}
// Middleware Express
function verifyTeoToken(req, res, next) {
const token = req.query.teo_jwt;
if (!token) {
return res.status(400).json({ error: 'Token manquant' });
}
jwt.verify(token, getKey, { algorithms: ['ES256'] }, (err, decoded) => {
if (err) {
return res.status(401).json({ error: 'Token invalide' });
}
req.teoUser = decoded;
next();
});
}
// Utilisation
app.get('/callback', verifyTeoToken, (req, res) => {
const { dossierId, dossierIid, userEmail } = req.teoUser;
// Votre logique métier...
});
Dépendances requises :
npm install jsonwebtoken jwks-rsa
Exemple Python
import jwt
import requests
from functools import lru_cache
@lru_cache(maxsize=1)
def get_jwks():
"""Récupère et met en cache les clés publiques."""
response = requests.get('https://auth.teoapp.fr/.well-known/jwks.json')
return response.json()
def get_public_key(token):
"""Récupère la clé publique correspondant au token."""
header = jwt.get_unverified_header(token)
jwks = get_jwks()
for key in jwks['keys']:
if key['kid'] == header['kid']:
return jwt.algorithms.RSAAlgorithm.from_jwk(key)
raise Exception('Clé non trouvée')
def verify_teo_token(token):
"""Vérifie et décode un token téo."""
try:
public_key = get_public_key(token)
decoded = jwt.decode(
token,
public_key,
algorithms=['ES256']
)
return decoded
except jwt.ExpiredSignatureError:
raise Exception('Token expiré')
except jwt.InvalidTokenError as e:
raise Exception(f'Token invalide: {e}')
# Utilisation (Flask)
@app.route('/callback')
def callback():
token = request.args.get('teo_jwt')
if not token:
return {'error': 'Token manquant'}, 400
try:
data = verify_teo_token(token)
dossier_id = data['dossierId']
user_email = data['userEmail']
# Votre logique métier...
except Exception as e:
return {'error': str(e)}, 401
Dépendances requises :
pip install PyJWT requests cryptography
Bonnes pratiques de sécurité
Validation obligatoire
- Vérifier la signature : Toujours valider la signature cryptographique du JWT
- Vérifier l'expiration : Le claim
expdoit être dans le futur - Vérifier l'émetteur : Le claim
issdoit correspondre à une instance téo autorisée
Recommandations
- HTTPS obligatoire : Toutes les communications doivent utiliser HTTPS
- Ne jamais faire confiance aux données non vérifiées : Valider le JWT avant d'utiliser ses données
- Logger les erreurs : Conserver une trace des tentatives d'authentification échouées
- Limiter les émetteurs : Maintenir une liste blanche des instances téo autorisées
// Exemple de vérification de l'émetteur
$allowedIssuers = [
'client1.teo-online.net',
'client2.teo-online.net',
];
if (!in_array($decoded->iss, $allowedIssuers)) {
throw new Exception('Émetteur non autorisé');
}
Environnement de test (Sandbox)
Un environnement de test est disponible pour valider votre intégration avant la mise en production.
Interface de test interactive
Une interface web est disponible pour générer des tokens de test et valider votre implémentation :
Accéder à l'interface de test Sandbox
Cette interface vous permet de :
- Générer des tokens JWT avec des données fictives
- Tester votre URL de callback
- Visualiser le payload décodé
- Valider votre implémentation sans dossier réel
API de test
Vous pouvez également générer des tokens de test par API :
POST https://{instance}.teo-online.net/next/app.php/api/authenticated-link/sandbox/token
Content-Type: application/json
{
"callback_url": "https://votre-app.com/callback",
"ttl": 300
}
Réponse :
{
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 300,
"jwks_uri": "https://auth.teoapp.fr/.well-known/jwks.json",
"test_url": "https://votre-app.com/callback?teo_jwt=..."
}
Format identique à la production
Les tokens sandbox ont exactement le même format que les tokens de production. Cela vous permet de tester votre implémentation dans des conditions réelles.
Les seules différences sont les valeurs des données, qui sont fictives :
| Champ | Valeur sandbox |
|---|---|
dossierId | 00000000-0000-0000-0000-000000000001 |
dossierIid | 1 |
Puisque le format est identique, vous pouvez valider l'intégralité de votre code de parsing et de vérification de signature avec les tokens sandbox.
Demander un accès partenaire
Une fois vos tests validés, contactez-nous pour la mise en production :
- Rendez-vous sur le système de tickets
- Créez un ticket "Demande d'intégration partenaire"
- Joignez :
- Le nom de votre application
- L'URL de callback de production
- Les résultats de vos tests sandbox
- Notre équipe configurera votre accès et vous fournira votre identifiant partenaire
Résolution de problèmes
Erreurs courantes
| Erreur | Cause | Solution |
|---|---|---|
Token expiré | Le token a dépassé sa durée de validité | Les tokens expirent après 5 minutes. L'utilisateur doit retourner sur téo. |
Signature invalide | Clé publique incorrecte ou token altéré | Vérifiez que vous utilisez les bonnes clés JWKS |
Clé non trouvée | Le kid du token ne correspond à aucune clé | Videz le cache des clés et réessayez |
Émetteur non autorisé | L'iss ne fait pas partie de votre liste blanche | Ajoutez l'émetteur à votre configuration |
Debug
Pour déboguer un token, vous pouvez utiliser jwt.io :
- Collez le token dans le décodeur
- Vérifiez les claims dans le payload
- Attention : ne faites pas confiance aux données sans vérifier la signature !