Authentification

Cette page décrit comment utiliser l'API d'authentification de GestSIS pour créer et gérer les tokens d'accès et de rafraîchissement.

Vue d'ensemble

L'API d'authentification GestSIS utilise un système de tokens JWT (JSON Web Tokens) pour sécuriser les accès. Deux types de tokens sont utilisés :

  • Access Token : Token de courte durée (8 heures) utilisé pour authentifier les requêtes API
  • Refresh Token : Token de longue durée (30 jours) utilisé pour renouveler l'access token

Base URL

L'API d'authentification est accessible via :

http://auth.gestsis.ch/api/v1

En production, l'URL sera différente selon votre configuration.


1. Connexion (Login)

Endpoint

POST /api/v1/login

Description

Permet à un utilisateur de se connecter et d'obtenir un access token et un refresh token.

Corps de la requête

{
  "email": "utilisateur@example.com",
  "password": "motdepasse"
}

Réponse en cas de succès (200 OK)

{
  "message": "Successful login",
  "accessToken": "eyJ0eXAiOiJKV1QiLCJhbGc...",
  "refreshToken": "a1b2c3d4e5f6g7h8",
  "user": {
    "id": 1,
    "name": "Jean Dupont",
    "email": "utilisateur@example.com",
    "email_verified_at": "2025-01-01T10:00:00.000000Z",
    "created_at": "2025-01-01T10:00:00.000000Z",
    "updated_at": "2025-01-01T10:00:00.000000Z"
  }
}

Réponse en cas d'erreur (401 Unauthorized)

{
  "error": "invalid credentials"
}

Ou en cas de validation échouée :

{
  "error": {
    "email": ["The email field is required."],
    "password": ["The password field is required."]
  }
}

Exemple avec cURL

curl -X POST http://auth.gestsis.ch/api/v1/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "utilisateur@example.com",
    "password": "motdepasse"
  }'

Exemple avec JavaScript (fetch)

const response = await fetch('http://auth.gestsis.ch/api/v1/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    email: 'utilisateur@example.com',
    password: 'motdepasse'
  })
});

const data = await response.json();

if (response.ok) {
  console.log('Access Token:', data.accessToken);
  console.log('Refresh Token:', data.refreshToken);
  
  // Stocker les tokens (par exemple dans localStorage)
  localStorage.setItem('accessToken', data.accessToken);
  localStorage.setItem('refreshToken', data.refreshToken);
} else {
  console.error('Erreur de connexion:', data.error);
}

2. Rafraîchir le Token (Refresh Token)

Endpoint

POST /api/v1/refresh-token

Description

Permet de renouveler l'access token en utilisant le refresh token. Le refresh token est à usage unique : après utilisation, il est détruit et un nouveau refresh token est généré.

Corps de la requête

{
  "token": "a1b2c3d4e5f6g7h8"
}

Réponse en cas de succès (200 OK)

{
  "message": "Successful login",
  "accessToken": "eyJ0eXAiOiJKV1QiLCJhbGc...",
  "refreshToken": "x9y8z7w6v5u4t3s2",
  "user": {
    "id": 1,
    "name": "Jean Dupont",
    "email": "utilisateur@example.com",
    "email_verified_at": "2025-01-01T10:00:00.000000Z",
    "created_at": "2025-01-01T10:00:00.000000Z",
    "updated_at": "2025-01-01T10:00:00.000000Z"
  }
}

Réponse en cas d'erreur (401 Unauthorized)

{
  "error": "Refresh token expired"
}

Ou en cas de validation échouée :

{
  "error": {
    "token": ["The token field is required."]
  }
}

Exemple avec cURL

curl -X POST http://auth.gestsis.ch/api/v1/refresh-token \
  -H "Content-Type: application/json" \
  -d '{
    "token": "a1b2c3d4e5f6g7h8"
  }'

Exemple avec JavaScript (fetch)

const refreshToken = localStorage.getItem('refreshToken');

const response = await fetch('http://auth.gestsis.ch/api/v1/refresh-token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    token: refreshToken
  })
});

const data = await response.json();

if (response.ok) {
  // Mettre à jour les tokens stockés
  localStorage.setItem('accessToken', data.accessToken);
  localStorage.setItem('refreshToken', data.refreshToken);
  
  console.log('Tokens renouvelés avec succès');
} else {
  console.error('Erreur de rafraîchissement:', data.error);
  // Si le refresh token a expiré, rediriger vers la page de connexion
  if (data.error === 'Refresh token expired') {
    window.location.href = '/login';
  }
}

Utilisation des Tokens

Structure de l'Access Token

L'access token est un JWT (JSON Web Token) signé avec RSA-256 qui contient les informations suivantes :

Structure complète

{
  "iss": "GestSIS_Auth",
  "aud": "GestSIS_API",
  "iat": 1702290000,
  "nbf": 1702289990,
  "exp": 1702318800,
  "data": {
    "id": 1,
    "admin": false,
    "validated": true,
    "pseudo": "Jean Dupont",
    "email": "jean.dupont@example.com",
    "permissions": {
      "test": [
        "intervention.lecture",
        "intervention.modification",
        "sapeur.lecture"
      ],
      "hs": [
        "intervention.lecture"
      ]
    },
    "mobiles": [
      "test",
      "hs"
    ],
    "sapeurs": {
      "test": 42,
      "hs": 108
    }
  }
}

Description des champs

Headers JWT standard :

  • iss (Issuer) : GestSIS_Auth - Émetteur du token
  • aud (Audience) : GestSIS_API - Destinataire du token
  • iat (Issued At) : Timestamp UNIX de la création du token
  • nbf (Not Before) : Timestamp UNIX à partir duquel le token est valide (10 secondes avant iat)
  • exp (Expiration) : Timestamp UNIX d'expiration (8 heures après iat)

Données utilisateur (data) :

  • id : Identifiant unique de l'utilisateur dans GestSIS_Auth
  • admin : Booléen indiquant si l'utilisateur est administrateur (a tous les droits sur tous les SIS)
  • validated : Booléen indiquant si l'email de l'utilisateur a été vérifié
  • pseudo : Nom d'affichage de l'utilisateur
  • email : Adresse email de l'utilisateur

Données multi-SIS :

  • permissions : Objet où chaque clé est l'api_key d'un SIS, contenant un tableau des permissions pour ce SIS
    • Exemple : {"test": ["intervention.lecture", "sapeur.modification"]}
    • Si l'utilisateur est admin, toutes les clés SIS contiennent ["admin"]
  • mobiles : Tableau simple contenant les api_key des SIS pour lesquels l'utilisateur peut recevoir des alertes mobiles
    • Exemple : ["test", "hs"]
  • sapeurs : Objet où chaque clé est l'api_key d'un SIS, contenant l'ID du sapeur associé à l'utilisateur dans ce SIS
    • Exemple : {"test": 42, "hs": 108}

Architecture multi-tenant

Important : Les permissions, mobiles et sapeurs sont organisés par SIS. Cela permet à un utilisateur :

  • D'avoir des permissions différentes selon le SIS
  • D'être sapeur dans plusieurs SIS avec des IDs différents
  • De recevoir des alertes mobiles uniquement pour certains SIS

Exemple : Un utilisateur peut être chef d'intervention dans le SDIS Jura mais simple sapeur dans le SDIS Neuchâtel.

Utiliser l'Access Token dans les requêtes API

Pour authentifier une requête API, incluez l'access token dans le header Authorization :

Authorization: Bearer {accessToken}

Exemple complet d'utilisation

// 1. Connexion
const loginResponse = await fetch('http://auth.gestsis.ch/api/v1/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: 'utilisateur@example.com',
    password: 'motdepasse'
  })
});

const loginData = await loginResponse.json();
localStorage.setItem('accessToken', loginData.accessToken);
localStorage.setItem('refreshToken', loginData.refreshToken);

// 2. Utiliser l'access token pour une requête protégée
const apiResponse = await fetch('http://apis.gestsis.ch/api/v1/protected-resource', {
  headers: {
    'Authorization': `Bearer ${localStorage.getItem('accessToken')}`
  }
});

// 3. Si le token a expiré (erreur 401), le rafraîchir
if (apiResponse.status === 401) {
  const refreshResponse = await fetch('http://auth.gestsis.ch/api/v1/refresh-token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      token: localStorage.getItem('refreshToken')
    })
  });
  
  const refreshData = await refreshResponse.json();
  localStorage.setItem('accessToken', refreshData.accessToken);
  localStorage.setItem('refreshToken', refreshData.refreshToken);
  
  // Réessayer la requête avec le nouveau token
  const retryResponse = await fetch('http://apis.gestsis.ch/api/v1/protected-resource', {
    headers: {
      'Authorization': `Bearer ${localStorage.getItem('accessToken')}`
    }
  });
}

Bonnes Pratiques

Sécurité

  1. Ne jamais exposer les tokens : Ne jamais afficher les tokens dans les logs ou les messages d'erreur
  2. Stockage sécurisé : Utiliser des mécanismes sécurisés pour stocker les tokens (httpOnly cookies pour le web)
  3. HTTPS uniquement : Toujours utiliser HTTPS en production pour éviter l'interception des tokens
  4. Refresh token à usage unique : Le système utilise des refresh tokens à usage unique pour prévenir le vol de tokens

Gestion des Tokens

  1. Renouvellement automatique : Implémenter un mécanisme de renouvellement automatique avant l'expiration
  2. Gestion des erreurs : Rediriger vers la page de connexion si le refresh token a expiré
  3. Déconnexion : Supprimer les tokens stockés lors de la déconnexion
// Fonction utilitaire pour gérer le renouvellement automatique
async function fetchWithAuth(url, options = {}) {
  const token = localStorage.getItem('accessToken');
  
  const response = await fetch(url, {
    ...options,
    headers: {
      ...options.headers,
      'Authorization': `Bearer ${token}`
    }
  });
  
  // Si le token a expiré, le rafraîchir et réessayer
  if (response.status === 401) {
    const refreshToken = localStorage.getItem('refreshToken');
    const refreshResponse = await fetch('http://auth.gestsis.ch/api/v1/refresh-token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ token: refreshToken })
    });
    
    if (refreshResponse.ok) {
      const data = await refreshResponse.json();
      localStorage.setItem('accessToken', data.accessToken);
      localStorage.setItem('refreshToken', data.refreshToken);
      
      // Réessayer la requête originale
      return fetch(url, {
        ...options,
        headers: {
          ...options.headers,
          'Authorization': `Bearer ${data.accessToken}`
        }
      });
    } else {
      // Refresh token expiré, rediriger vers login
      window.location.href = '/login';
      throw new Error('Session expirée');
    }
  }
  
  return response;
}

Durée de vie des Tokens

  • Access Token : 8 heures
  • Refresh Token : 30 jours
  • Reset Token : 1 heure
  • Confirmation Token : 30 jours

Autres Endpoints d'Authentification

Inscription

POST /api/v1/register

Permet de créer un nouveau compte utilisateur.

Confirmation d'Email

POST /api/v1/confirmer-email

Permet de confirmer l'adresse email d'un nouvel utilisateur.

Mot de Passe Oublié

POST /api/v1/forgotten-password

Demande un lien de réinitialisation de mot de passe.

Réinitialiser le Mot de Passe

POST /api/v1/reset-password

Réinitialise le mot de passe avec un token de réinitialisation.

Changer le Mot de Passe

POST /api/v1/change-password

Permet à un utilisateur authentifié de changer son mot de passe.