C
Docs

Sécurité de l'API

Guide complet pour sécuriser les intégrations API avec Cothon incluant l'authentification JWT, les jetons API, la limitation de débit, les politiques CORS, la sécurité des webhooks et les meilleures pratiques

Updated 2026-03-3033 min read

Sécurité de l'API

L'API REST de Cothon fournit un accès programmatique aux données d'intelligence d'approvisionnement, permettant des intégrations avec vos systèmes d'affaires existants, des tableaux de bord personnalisés et des flux de travail automatisés. La sécurité est primordiale lors de l'exposition de données d'appels d'offres sensibles via API, donc nous avons mis en œuvre plusieurs couches de protection : authentification robuste, autorisation granulaire, limitation de débit et journalisation d'audit complète.

Ce guide couvre tout ce dont vous avez besoin pour intégrer de manière sécurisée avec l'API de Cothon, de la génération et la gestion des jetons API à la mise en œuvre de la sécurité des webhooks et à la gestion gracieuse des erreurs.

Authentification API

Tous les points de terminaison de l'API Cothon nécessitent une authentification. Nous prenons en charge deux méthodes d'authentification selon votre cas d'utilisation.

Authentification par jeton JWT (Sessions utilisateur)

Lorsque les utilisateurs interagissent avec l'application Web Cothon, l'authentification utilise des jetons Web JSON (JWT) émis lors de la connexion.

Note

Quand utiliser l'authentification JWT : Utilisez les jetons JWT pour les applications Web et mobiles où les utilisateurs se connectent de manière interactive. Les JWT fournissent un accès basé sur la session lié à des identités d'utilisateur spécifiques.

Pour les intégrations serveur à serveur, les scripts ou les pipelines CI/CD où il n'y a pas d'utilisateur interactif, utilisez plutôt l'authentification par jeton API.

Authentification par jeton API (Comptes de service)

Les jetons API fournissent un accès programmatique pour les systèmes automatisés, les scripts et les intégrations sans nécessiter d'identifiants utilisateur.

Avertissement

Réponse à la compromission de jeton : Si vous soupçonnez qu'un jeton API a été compromis :

  1. Révoquer immédiatement : Paramètres → Sécurité → Jetons API → Révoquer le jeton compromis
  2. Consulter les journaux d'audit : Vérifiez Paramètres → Sécurité → Journaux d'audit pour activité suspecte utilisant ce jeton
  3. Générer un nouveau jeton : Créez un jeton de remplacement avec les mêmes permissions
  4. Enquêter : Déterminez comment le jeton a été compromis (historique Git, journaux, violation de serveur)
  5. Notifier l'équipe de sécurité : Si la compromission a affecté des données sensibles, contactez security@cothon.ca

Les jetons compromis peuvent être utilisés pour accéder à toutes les données au sein de votre organisation jusqu'à révocation. Agissez rapidement.

Format et structure des jetons

Les jetons JWT et API suivent des formats spécifiques :

Jetons d'accès JWT :

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyX2FiYzEyMyIsImVtYWlsIjoiamFuZS5zbWl0aEBhY21lY29ycC5jb20iLCJvcmdhbml6YXRpb25zIjpbIm9yZ194eXo3ODkiXSwicm9sZSI6Im1lbWJlciIsImlhdCI6MTcxMTgwMDAwMCwiZXhwIjoxNzExODAwOTAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Structure : <en-tête>.<charge-utile>.<signature>

  • En-tête : Algorithme et type de jeton
  • Charge utile : ID utilisateur, courriel, organisations, rôle, expiration
  • Signature : Signature HMAC-SHA256 pour vérification

Jetons API :

cothon_live_3kT9wM2pN5qR7sX1vY4zW6bC8dE0fG2hJ4kL6mN8oP0qR2sT4uV6wX8yZ

Format : cothon_<env>_<chaîne_aléatoire>

  • Préfixe : cothon_ identifie les jetons Cothon (pour l'analyse de secrets)
  • Environnement : live (production) ou test (sandbox)
  • Chaîne aléatoire : Chaîne cryptographiquement aléatoire de 58 caractères

Jetons de test vs. jetons actifs :

N'utilisez jamais de jetons actifs dans les environnements de développement/test ou ne les validez pas dans le contrôle de version.

Autorisation API

L'authentification vérifie qui vous êtes ; l'autorisation détermine ce que vous pouvez faire.

Sécurité au niveau des lignes (RLS)

Cothon utilise la sécurité au niveau des lignes PostgreSQL pour appliquer les politiques d'accès aux données au niveau de la base de données.

Chaque table a des politiques RLS qui filtrent les données en fonction de l'utilisateur authentifié :

-- Exemple : Les utilisateurs peuvent uniquement accéder aux analyses d'appels d'offres de leurs organisations
CREATE POLICY "organisation_isolation"
ON bid_analyses FOR SELECT
USING (
  organization_id IN (
    SELECT organization_id
    FROM organization_members
    WHERE user_id = auth.uid()
  )
);

-- Exemple : Seuls les Admins et Propriétaires peuvent supprimer les analyses
CREATE POLICY "admin_delete"
ON bid_analyses FOR DELETE
USING (
  EXISTS (
    SELECT 1
    FROM organization_members
    WHERE user_id = auth.uid()
    AND organization_id = bid_analyses.organization_id
    AND role IN ('admin', 'owner')
  )
);

Avantages du RLS :

  • Défense en profondeur : Même si les contrôles d'accès au niveau de l'application sont contournés, la base de données applique les politiques
  • Application automatique : Pas besoin d'ajouter WHERE organization_id = ? à chaque requête
  • Piste d'audit : Les vérifications RLS échouées sont journalisées pour la surveillance de sécurité

Modèle de permissions

L'accès API est contrôlé par les rôles d'organisation :

Point de terminaisonObservateurMembreAdminPropriétaire
GET /api/v1/bid-analyses
POST /api/v1/bid-analysis/analyze
PUT /api/v1/bid-analyses/:id
DELETE /api/v1/bid-analyses/:id
GET /api/v1/opportunities
POST /api/v1/opportunities/:id/analyze
POST /api/v1/ai/generate-proposal
GET /api/v1/export/organization
POST /api/v1/sharing/shared-analyses
GET /api/v1/audit-logs
POST /api/v1/organization/members
DELETE /api/v1/organization/members/:id
DELETE /api/v1/organization

Mappage de portée de jeton API :

  • Jetons en lecture seule : Équivalent au rôle Observateur (demandes GET uniquement)
  • Jetons en lecture-écriture : Équivalent au rôle Membre (GET, POST, PUT, PATCH)
  • Jetons Admin : Équivalent au rôle Admin (inclut DELETE, gestion utilisateur)

Réponses d'erreur

Lorsque l'autorisation échoue, l'API retourne des réponses d'erreur claires :

403 Interdit (authentifié mais non autorisé) :

{
  "error": "forbidden",
  "message": "Vous n'avez pas la permission de supprimer les analyses d'appels d'offres. Rôle requis : Admin ou Propriétaire. Votre rôle : Membre.",
  "code": "INSUFFICIENT_PERMISSIONS",
  "required_role": "admin",
  "current_role": "member"
}

401 Non autorisé (échec d'authentification) :

{
  "error": "unauthorized",
  "message": "Jeton invalide ou expiré. Veuillez vous authentifier et réessayer.",
  "code": "INVALID_TOKEN"
}

404 Non trouvé (la ressource existe mais vous ne pouvez pas y accéder) :

{
  "error": "not_found",
  "message": "Analyse d'appel d'offres non trouvée.",
  "code": "RESOURCE_NOT_FOUND"
}

Note : Nous retournons 404 (pas 403) lorsque vous essayez d'accéder à des ressources d'autres organisations. Cela empêche de divulguer des informations sur l'existence de ressources.

Conseil

Débogage des erreurs d'autorisation : Si vous recevez des erreurs 403 inattendues :

  1. Vérifiez votre rôle : Vérifiez votre rôle d'organisation dans Paramètres → Organisation → Membres de l'équipe
  2. Vérifiez la portée du jeton : Vérifiez la portée du jeton API dans Paramètres → Sécurité → Jetons API
  3. Consultez les journaux d'audit : Vérifiez Paramètres → Sécurité → Journaux d'audit pour les vérifications de permissions échouées
  4. Testez avec l'interface Web : Si l'opération fonctionne dans l'interface Web mais échoue via API, contactez support@cothon.ca

Problèmes courants :

  • Utilisation d'un jeton en lecture seule pour des opérations d'écriture
  • Rôle Membre essayant de supprimer des éléments (nécessite Admin)
  • Accès à des ressources d'organisations dont vous n'êtes pas membre

Limitation de débit

Pour assurer une utilisation équitable et prévenir les abus, Cothon met en œuvre une limitation de débit sur tous les points de terminaison de l'API.

Niveaux de limitation de débit

Les limites de débit varient selon le plan d'abonnement et le type de point de terminaison :

PlanPoints de terminaison standardPoints de terminaison IA/intensifs en calculTolérance de rafale
Gratuit60 req/min, 1 000 req/jour5 req/min, 50 req/jour10 demandes
Professionnel300 req/min, 10 000 req/jour20 req/min, 200 req/jour50 demandes
Entreprise1 000 req/min, 50 000 req/jour100 req/min, 1 000 req/jour200 demandes
PersonnaliséNégociableNégociableNégociable

Points de terminaison standard : GET, POST, PUT, DELETE pour analyses d'appels d'offres, propositions, opportunités, données d'organisation

Points de terminaison IA/intensifs en calcul :

  • /api/v1/bid-analysis/analyze (analyse PDF)
  • /api/v1/ai/generate-proposal (génération de proposition)
  • /api/v1/full-extraction/extract (extraction complète de document)
  • /api/v1/rag/search (recherche sémantique)

Tolérance de rafale : Pic à court terme de demandes au-delà de la limite par minute. Une fois épuisée, les demandes sont limitées au taux par minute.

En-têtes de limitation de débit

Chaque réponse API inclut des en-têtes de limitation de débit :

HTTP/1.1 200 OK
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1711800120
X-RateLimit-Bucket: standard
Retry-After: 42
En-têteDescriptionExemple
X-RateLimit-LimitDemandes maximales par fenêtre300 (demandes par minute)
X-RateLimit-RemainingDemandes restantes dans la fenêtre actuelle287
X-RateLimit-ResetHorodatage Unix quand la limite se réinitialise1711800120 (secondes epoch)
X-RateLimit-BucketCatégorie de limitation de débitstandard, ai-compute
Retry-AfterSecondes avant de pouvoir réessayer (uniquement sur erreurs 429)42

Gestion des limitations de débit

Lorsque vous dépassez les limites de débit, l'API retourne 429 Trop de demandes :

{
  "error": "rate_limit_exceeded",
  "message": "Limite de débit dépassée. Vous avez fait 301 demandes dans la minute actuelle. Limite : 300 demandes par minute.",
  "code": "RATE_LIMIT_EXCEEDED",
  "retry_after_seconds": 42,
  "limit": 300,
  "window": "1 minute",
  "reset_at": "2026-03-30T14:25:20Z"
}

Succès

Augmentations de limite de débit : Si votre cas d'utilisation légitime nécessite des limites de débit plus élevées, contactez enterprise@cothon.ca avec les détails :

  • Votre plan actuel et utilisation de l'API
  • Description de votre intégration
  • Volume de demandes attendu (demandes par minute/jour)
  • Points de terminaison spécifiques que vous appelez

Nous pouvons fournir des limites de débit personnalisées pour les clients d'entreprise et les intégrations à fort volume.

Meilleures pratiques de limitation de débit

Politiques CORS

Le partage de ressources entre origines (CORS) contrôle quelles origines Web peuvent accéder à l'API Cothon depuis les navigateurs.

Origines autorisées

Par défaut, Cothon autorise les demandes CORS depuis :

  • https://app.cothon.ca (frontend de production)
  • https://sandbox.cothon.ca (frontend sandbox)
  • http://localhost:3000 (développement local)
  • http://localhost:5173 (serveur de développement Vite)

Origines personnalisées (Entreprise) : Les plans Entreprise peuvent configurer des origines autorisées supplémentaires dans Paramètres → Intégrations → Paramètres CORS.

Exemples de cas d'utilisation :

  • Tableaux de bord personnalisés hébergés à https://dashboard.acmeentreprise.com
  • Outils internes à https://tools.acmeentreprise.internal
  • Portails partenaires à https://partners.acmeentreprise.com

En-têtes CORS

Lors de demandes API depuis les navigateurs, Cothon retourne des en-têtes CORS :

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.cothon.ca
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type, X-Request-ID
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
En-têteDescriptionValeur
Access-Control-Allow-OriginOrigine autorisée pour cette demandehttps://app.cothon.ca
Access-Control-Allow-MethodsMéthodes HTTP autoriséesGET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-HeadersEn-têtes de demande autorisésAuthorization, Content-Type, X-Request-ID
Access-Control-Allow-CredentialsAutoriser témoins/identifiantstrue
Access-Control-Max-AgeDurée de mise en cache de la réponse preflight86400 (24 heures)

Demandes preflight

Pour les demandes complexes (POST, PUT, DELETE, en-têtes personnalisés), les navigateurs envoient une demande OPTIONS preflight :

OPTIONS /api/v1/bid-analyses HTTP/1.1
Origin: https://dashboard.acmeentreprise.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type

Cothon répond avec les méthodes et en-têtes autorisés :

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://dashboard.acmeentreprise.com
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type, X-Request-ID
Access-Control-Max-Age: 86400

Le navigateur procède ensuite avec la demande réelle.

Avertissement

CORS et sécurité : CORS est une fonctionnalité de sécurité du navigateur. Elle n'empêche PAS les clients non-navigateurs (curl, Postman, scripts côté serveur) d'accéder à l'API.

Pour les intégrations côté serveur, CORS n'est pas pertinent — l'authentification (jetons API) est le contrôle de sécurité principal.

CORS protège uniquement contre les sites Web malveillants faisant des demandes API non autorisées depuis les navigateurs des utilisateurs.

Gestion des erreurs CORS

Si vous rencontrez des erreurs CORS dans les applications basées sur navigateur :

Erreur : L'accès à fetch à 'https://api.cothon.ca/api/v1/bid-analyses' depuis l'origine 'https://monapp.com' a été bloqué par la politique CORS

Solutions :

Sécurité des webhooks

Les webhooks permettent à Cothon de notifier vos systèmes d'événements (nouvelles analyses, génération de proposition terminée, correspondances d'opportunités) sans sondage.

Configuration de webhook

Types d'événements webhook

Cothon envoie des webhooks pour ces événements :

ÉvénementDescriptionCharge utile
analysis.createdNouvelle analyse d'appel d'offres démarréeanalysis_id, title, status, created_at
analysis.completedAnalyse d'appel d'offres terminéeanalysis_id, title, requirements_count, overall_score
analysis.failedAnalyse d'appel d'offres échouéeanalysis_id, error_message, failed_at
proposal.generatedProposition IA généréeproposal_id, analysis_id, word_count, generated_at
opportunity.matchedNouvelle opportunité correspondant aux capacitésopportunity_id, title, deadline, match_score
opportunity.deadline_soonÉchéance d'opportunité sauvegardée approchantopportunity_id, title, deadline, days_remaining
member.addedMembre de l'équipe ajouté à l'organisationuser_id, email, role, added_at
member.removedMembre de l'équipe retiré de l'organisationuser_id, email, removed_at

Sécurité des webhooks

Logique de nouvelle tentative webhook

Si votre point de terminaison retourne une erreur (réponse non-200 ou timeout), Cothon réessaie le webhook :

TentativeDélaiTemps total
1Immédiat0s
230 secondes30s
35 minutes5m 30s
430 minutes35m 30s
52 heures2h 35m 30s

Après 5 tentatives échouées, le webhook est marqué comme échoué et vous recevrez une alerte par courriel.

Voir les webhooks échoués : Accédez à Paramètres → Intégrations → Webhooks → « Livraisons échouées » pour voir les webhooks qui ont échoué toutes les tentatives.

Vous pouvez réessayer manuellement les webhooks échoués en cliquant sur « Réessayer la livraison ».

Conseil

Test de webhook : Utilisez des outils comme ngrok pour exposer des serveurs de développement locaux pour le test de webhook :

# Démarrer le serveur local sur le port 5000
python app.py

# Exposer via ngrok
ngrok http 5000

Utilisez l'URL ngrok (par ex. https://abc123.ngrok.io/webhooks/cothon) comme point de terminaison webhook dans les paramètres Cothon.

Alternativement, utilisez des services de test de webhook comme webhook.site pour inspecter les charges utiles webhook sans écrire de code.

Meilleures pratiques API

Gestion des erreurs

Implémentez une gestion complète des erreurs pour toutes les demandes API :

import requests

def make_api_request(url, headers, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = requests.get(url, headers=headers, timeout=30)

            if response.status_code == 200:
                return response.json()

            elif response.status_code == 401:
                raise AuthenticationError("Jeton API invalide ou expiré")

            elif response.status_code == 403:
                raise PermissionError(f"Permissions insuffisantes : {response.json().get('message')}")

            elif response.status_code == 404:
                return None  # Ressource non trouvée

            elif response.status_code == 429:
                retry_after = int(response.headers.get('Retry-After', 60))
                time.sleep(retry_after)
                continue

            elif response.status_code >= 500:
                # Erreur serveur, réessayer avec backoff
                wait_time = 2 ** attempt
                time.sleep(wait_time)
                continue

            else:
                response.raise_for_status()

        except requests.exceptions.Timeout:
            if attempt == max_retries - 1:
                raise TimeoutError(f"Demande expirée après {max_retries} tentatives")
            time.sleep(2 ** attempt)

        except requests.exceptions.ConnectionError:
            if attempt == max_retries - 1:
                raise ConnectionError("Impossible de se connecter à l'API Cothon")
            time.sleep(2 ** attempt)

    raise Exception(f"Demande échouée après {max_retries} tentatives")

Journalisation et surveillance

Journalisez toutes les interactions API pour le débogage et la surveillance :

import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def make_api_request(url, headers):
    logger.info(f"Demande API : GET {url}")

    try:
        response = requests.get(url, headers=headers, timeout=30)

        logger.info(f"Réponse API : {response.status_code} - {len(response.content)} octets")

        if response.status_code != 200:
            logger.warning(f"Erreur API : {response.status_code} - {response.text}")

        return response

    except Exception as e:
        logger.error(f"Exception API : {str(e)}", exc_info=True)
        raise

Métriques à suivre :

  • Nombre de demandes par point de terminaison
  • Taux de succès/erreur
  • Temps de réponse (p50, p95, p99)
  • Utilisation de la limitation de débit
  • Utilisation des jetons API par jeton

Utilisez des outils de surveillance comme Prometheus, Datadog ou New Relic pour suivre la santé de l'API.

ID de demande

Incluez un ID de demande unique dans chaque appel API pour le débogage :

import uuid

def make_api_request(url, headers):
    request_id = str(uuid.uuid4())
    headers['X-Request-ID'] = request_id

    logger.info(f"Demande {request_id} : GET {url}")

    response = requests.get(url, headers=headers)

    logger.info(f"Demande {request_id} : {response.status_code}")

    return response

Si vous rencontrez des erreurs, fournissez le X-Request-ID au support pour un dépannage plus rapide.

Pagination

Utilisez la pagination pour les points de terminaison qui retournent de grands ensembles de résultats :

def fetch_all_analyses(headers):
    analyses = []
    page = 1
    per_page = 100

    while True:
        response = requests.get(
            'https://api.cothon.ca/api/v1/bid-analyses',
            headers=headers,
            params={'page': page, 'per_page': per_page}
        )

        data = response.json()
        analyses.extend(data['items'])

        if not data.get('has_more'):
            break

        page += 1

    return analyses

Limites de pagination :

  • per_page maximum : 100 éléments
  • per_page par défaut : 20 éléments

Utilisez per_page=100 pour minimiser les appels API.

Foire aux questions

Ressources supplémentaires

Support API :

Ressources développeur :


Dernière mise à jour : 30 mars 2026

Prochaine révision : 30 juin 2026

Was this page helpful?

Sécurité de l'API | Cothon Docs | Cothon