# SmartAuth
SmartAuth est le module d'authentification JWT pour Dolibarr, conçu pour les applications SmartMaker.
## Pourquoi SmartAuth ?
Nativement Dolibarr propose une API où chaque utilisateur dispose d'une seule clé d'API donnant accès à tout son périmètre fonctionnel.
Ce fonctionnement pose problème : si vous développez une application mobile qui ne devrait avoir accès qu'à l'agenda de l'utilisateur, avec la clé native l'application pourra également accéder aux factures et autres éléments.
### Notre approche
Avec SmartAuth, un utilisateur peut avoir **autant de clés d'API qu'il souhaite**, chaque clé ayant ses propres droits. Si une clé est liée à une application, elle ne sera pas réutilisable par une autre.
Avantages :
* **Cloisonnement des accès** : chaque application a sa propre clé
* **Révocation ciblée** : si un appareil est volé, supprimez uniquement sa clé
* **Traçabilité** : journaux de connexion par appareil
{{.:pasted:20250903-082553.png?direct}}
### Journaux de connexion
Accédez aux journaux pour détecter des actions incorrectes et gérer les accès :
{{.:pasted:20250903-082923.png?direct}}
## Installation
Téléchargez SmartAuth gratuitement sur le DoliStore : https://www.dolistore.com/product.php?id=2509&l=fr
## Flux d'authentification JWT
### Schéma global
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Mobile │ │ api.php │ │ Dolibarr │
│ (React) │ │ (JWT) │ │ (PHP) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
│ 1. POST /login │ │
│ {login, password} │ │
│──────────────────────>│ │
│ │ 2. Vérifier user │
│ │──────────────────────>│
│ │ │
│ │ 3. User valide │
│ │<──────────────────────│
│ 4. {accessToken, │ │
│ refreshToken} │ │
│<──────────────────────│ │
│ │ │
│ 5. GET /items │ │
│ Authorization: Bearer│ │
│──────────────────────>│ │
│ │ 6. Valider token │
│ │ + charger user │
│ │──────────────────────>│
│ │ │
│ 7. Données │ │
│<──────────────────────│ │
```
### Tokens JWT
SmartAuth utilise deux tokens :
^ Token ^ Durée ^ Usage ^
| ''accessToken'' | 15 minutes | Authentifier les requêtes API |
| ''refreshToken'' | 7 jours | Obtenir un nouveau accessToken |
### Endpoints d'authentification
^ Méthode ^ Route ^ Description ^ Protégé ^
| GET | ''/login'' | Récupérer les infos de connexion (logo, etc.) | Non |
| POST | ''/login'' | Authentification (login + password) | Non |
| GET | ''/refresh'' | Renouveler l'accessToken | Non |
| POST | ''/logout'' | Déconnexion (invalide le refreshToken) | Oui |
| POST | ''/device'' | Enregistrer un appareil pour les notifications | Oui |
| POST | ''/qr-pair/{pairingId}/claim'' | Mobile revendique un pairing affiché par le PC | Non |
| POST | ''/qr-pair/{pairingId}/poll'' | Mobile poll jusqu'à confirmation du PC | Non |
## Configuration côté api.php
### Routes d'authentification
```
Ne modifiez pas ce fichier sauf si vous savez ce que vous faites.
## Utilisation côté React
### Configuration du Provider
```
// appConfig.js
export const config = {
api: {
prefixUrl: import.meta.env.VITE_API_URL,
paths: {
login: "login",
logout: "logout",
refresh: "refresh",
},
},
};
```
### Login avec useApi (manuel)
```
import { useApi, useGlobalStates } from '@cap-rel/smartcommon';
const LoginPage = () => {
const api = useApi();
const gst = useGlobalStates();
const handleLogin = async (data) => {
try {
const user = await api.login(data);
gst.local.set('session', user);
} catch (error) {
console.error('Login failed:', error);
}
};
return (
);
};
```
### Login simplifié avec LoginComponent
Plutôt que de réinventer le formulaire, smartcommon expose '''' qui encapsule le flux complet : email + mot de passe + sélection d'entité optionnelle + **scan QR pair smartAuth** :
```
import { LoginComponent } from '@cap-rel/smartcommon';
navigate("/")}
onError={(err) => log.error(err)}
// QR pair activé par défaut. Désactiver explicitement si le backend
// n'expose pas /qr-pair (cas non-smartAuth).
enableQrPair
showRememberMe
labels={{
emailLabel: t("login.email"),
submitLabel: t("login.submit"),
scanQrLabel: t("login.scan-qr"),
}}
/>
```
Voir [[:03_front:composants_avances#logincomponent|Composants avancés -> LoginComponent]] pour les libellés, slots de styling et options du flux QR pair.
### Refresh automatique
Le hook ''useApi'' gère automatiquement le refresh du token :
```
// Quand l'accessToken expire, useApi :
// 1. Intercepte l'erreur 401
// 2. Appelle GET /refresh avec le refreshToken
// 3. Met à jour les tokens en session
// 4. Rejoue la requête originale
```
### Logout
```
const handleLogout = async () => {
await api.private.post('logout');
setSession(null);
navigate('/login');
};
```
## Gestion des appareils
### Enregistrer un appareil
Pour les notifications push, enregistrez l'appareil après le login :
```
const registerDevice = async (pushToken) => {
await api.private.post('device', {
json: {
token: pushToken,
platform: 'android', // ou 'ios', 'web'
name: 'Mon téléphone',
},
});
};
```
### Structure côté Dolibarr
SmartAuth stocke les appareils dans une table dédiée :
^ Champ ^ Description ^
| ''fk_user'' | ID de l'utilisateur Dolibarr |
| ''token'' | Token push de l'appareil |
| ''platform'' | Plateforme (android, ios, web) |
| ''name'' | Nom de l'appareil |
| ''last_used'' | Date de dernière utilisation |
### Identification d'appareil (DeviceIdentificationComponent)
Quand un utilisateur se connecte sur un appareil non encore enregistré, SmartAuth peut renvoyer un ''user.deviceOptions'' qui liste ses appareils existants pour qu'il choisisse de pairer ou d'en créer un nouveau.
Le composant '''' encapsule ce flux :
```
import { DeviceIdentificationComponent } from '@cap-rel/smartcommon';
navigate("/")}
onError={(err) => toast.error(err.message)}
labels={{ title: t("device.title"), submitLabel: t("device.submit") }}
/>
```
Au submit, ''api.identifyDevice({ label, uuid })'' est appelé. L'endpoint smartAuth nettoie ''user.deviceOptions'' côté serveur. Voir [[:03_front:composants_avances#deviceidentificationcomponent|détails]].
## QR pair (connexion par scan)
SmartAuth permet à un utilisateur de se connecter sur mobile en scannant un QR code affiché par un poste de travail déjà authentifié, sans saisir de mot de passe. Le mobile devient un appareil "de confiance" persisté localement.
### Endpoints
```
POST /qr-pair/{pairingId}/claim // Mobile revendique le pairing
POST /qr-pair/{pairingId}/poll // Mobile poll jusqu'à confirmation
```
### Côté useApi
''useApi()'' expose deux méthodes correspondantes :
```
const api = useApi();
// 1. Mobile revendique un pairing_id affiché par le PC
const { claim_token } = await api.claimQrPair(pairingId, {
device_label: "iPhone Eric",
device_uuid: "u-1",
});
// 2. Mobile poll jusqu'à confirmation côté PC
// Statuts : 'pending' | 'cancelled' | 'expired' | 'consumed'
const data = await api.pollQrPair(pairingId, claim_token);
// data.status === 'consumed' : user persisté automatiquement en local storage
```
Quand ''pollQrPair'' retourne ''consumed'', l'utilisateur est **persisté automatiquement** (par design : un appareil scanné est de confiance). Pas besoin d'appeler un helper d'auth.
### UX intégrée : LoginComponent
Pour ne pas réimplémenter ce flux dans chaque app, '''' (activé par défaut) gère scan -> claim -> poll avec :
* Garde d'idempotence (évite le 409 sur Android avec autofocus)
* Overlay plein écran avec spinner pendant claim/poll
* Bouton "Annuler" et timeout global
* Mapping d'erreurs personnalisable via ''getQrErrorLabel''
Voir [[:03_front:composants_avances#logincomponent|détails]].
### Référence backend
''~/dev/smartauth/api/QrPairController.php''. Codes d'erreur exposés via ''error.apiCode'' :
* ''pairing_not_claimable'' / 409 : QR déjà utilisé
* ''pairing_not_found'' / 404 : QR invalide
* ''pairing_expired'' / 410 : QR expiré
* ''rate_limited'' / 429 : trop de tentatives
* ''invalid_pairing_id'' / 400 : format incorrect
## Sécurité
### Bonnes pratiques
* **HTTPS obligatoire** : Les tokens JWT transitent en clair
* **Stockage sécurisé** : Utilisez ''useGlobalStates'' qui persiste en localStorage chiffré
* **Rotation des tokens** : Le refreshToken est à usage unique
* **Révocation** : Supprimez les clés depuis l'interface Dolibarr
### En cas de vol d'appareil
1. Connectez-vous à Dolibarr
2. Allez dans votre profil utilisateur
3. Supprimez la clé API de l'appareil volé
4. Les tokens associés seront immédiatement invalidés
## Réinitialisation du mot de passe
SmartAuth inclut un système complet de réinitialisation de mot de passe par token.
### Endpoints
^ Méthode ^ Route ^ Description ^ Protégé ^
| POST | ''/password/reset'' | Demande de réinitialisation (envoi d'email) | Non |
| POST | ''/password/confirm'' | Confirmation avec token | Non |
| POST | ''/password/change'' | Changement de mot de passe (utilisateur connecté) | Oui |
### Demander une réinitialisation
```
// Côté React
const requestReset = async (email) => {
await api.public.post('password/reset', {
json: { email }
});
};
```
Le serveur :
- Vérifie l'utilisateur (rate limiting : 3 tentatives par 15 minutes)
- Génère un token avec expiration (1 heure)
- Envoie un email avec le lien de réinitialisation
### Confirmer la réinitialisation
```
const confirmReset = async (token, newPassword) => {
await api.public.post('password/confirm', {
json: { token, password: newPassword }
});
};
```
### Changer le mot de passe (utilisateur connecté)
```
const changePassword = async (oldPassword, newPassword) => {
await api.private.post('password/change', {
json: {
current_password: oldPassword,
new_password: newPassword
}
});
};
```
## Fichiers temporaires (SmartTempFile)
SmartAuth fournit un système de fichiers temporaires pour le téléchargement de binaires (exports Excel, PDF générés, etc.).
### Endpoints
^ Méthode ^ Route ^ Description ^
| GET | ''/temp-file/{token}'' | Téléchargement base64 (JSON) |
| GET | ''/temp-file/{token}/binary'' | Téléchargement binaire (stream) |
| DELETE | ''/temp-file/{token}'' | Suppression du fichier |
### Utilisation côté PHP (Controller)
```
use SmartAuth\Api\SmartTempFile;
public function exportExcel($payload)
{
// Générer le fichier
$filePath = '/tmp/export.xlsx';
// ... génération du fichier ...
// Stocker en fichier temporaire (TTL défaut : 1 heure)
$tempFile = new SmartTempFile();
$token = $tempFile->store($filePath, [
'filename' => 'export.xlsx',
'mimetype' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'user_id' => $payload['user_id'],
'entity' => $payload['entity'],
]);
return [['token' => $token], 200];
}
```
### Utilisation côté React
```
// Téléchargement binaire
const downloadExport = async (token) => {
const response = await api.get(`temp-file/${token}/binary`, { raw: true });
const blob = await response.blob();
// Créer un lien de téléchargement
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'export.xlsx';
a.click();
URL.revokeObjectURL(url);
};
```
## CORS
SmartAuth gère automatiquement les headers CORS pour les requêtes cross-origin.
### Configuration
^ Constante Dolibarr ^ Description ^ Défaut ^
| ''SMARTAUTH_CORS_ORIGIN'' | Origine autorisée | ''*'' |
| ''SMARTAUTH_CORS_METHODS'' | Méthodes autorisées | ''GET, POST, PUT, DELETE, PATCH, OPTIONS'' |
| ''SMARTAUTH_CORS_HEADERS'' | Headers autorisés | ''Content-Type, Authorization, X-DeviceId'' |
Les requêtes OPTIONS (preflight) sont gérées automatiquement.
En production, configurez ''SMARTAUTH_CORS_ORIGIN'' avec l'URL exacte de votre application au lieu de ''*''.
## Proxys de confiance
Si votre Dolibarr est derrière un reverse proxy (nginx, Apache, etc.), configurez les IP de confiance pour que SmartAuth détecte correctement l'adresse IP du client :
^ Constante ^ Description ^
| ''SMARTAUTH_TRUSTED_PROXIES'' | Liste d'IP séparées par des virgules (ex: ''10.0.0.1,192.168.1.100'') |
Les IP privées (127.x, 10.x, 172.16-31.x, 192.168.x) sont automatiquement considérées comme des proxys de confiance.
## Routes locales (LocalRoutes)
SmartAuth définit des routes locales automatiquement chargées. Elles n'ont pas besoin d'être déclarées dans votre ''api.php'' :
^ Groupe ^ Routes ^
| Auth | ''/login'', ''/refresh'', ''/logout'', ''/device'' |
| Mot de passe | ''/password/reset'', ''/password/confirm'', ''/password/change'' |
| Fichiers | ''/file/{hash}'', ''/file/{hash}/binary'' |
| Fichiers temporaires | ''/temp-file/{token}'', ''/temp-file/{token}/binary'' |
| Synchronisation | ''/sync/register'', ''/sync/pull'', ''/sync/push'', ''/sync/status'', ''/sync/conflicts'' |
| PWA | ''/manifest'', ''/icon/{size}'' |
## Voir aussi
* [[../back/start|Back (PHP)]] - Routes et Controllers
* [[../front/requetes_api|Requêtes API]] - Utilisation de useApi
* [[../front/configuration|Configuration]] - Configuration du Provider
* Dépôt SmartAuth : https://inligit.fr/cap-rel/dolibarr/plugin-smartauth/