# 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.