> For the complete documentation index, see [llms.txt](https://cours.davidannebicque.fr/sae401/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cours.davidannebicque.fr/sae401/echanges-front-back.md).

# Echanges front/back

{% hint style="info" %}

Cette page a été partiellement produite par l'IA.
{% endhint %}

## 1. Pourquoi JWT ?

Dans une architecture moderne :

* le **front Vue.js** gère l’interface utilisateur,
* le **backend Symfony** expose une API,

👉 On utilise donc un **token** transmis à chaque requête.

Le [JWT](https://jwt.io/) (*JSON Web Token*) permet :

✅ authentification sans session\
✅ API stateless\
✅ séparation claire front / back\
✅ compatibilité SPA / mobile / microservices

## 2. Principe global

### Cycle complet

<figure><img src="/files/STI5zzgVhpMB29QoUXJD" alt=""><figcaption><p><a href="https://stackoverflow.com/questions/65351531/what-is-the-flow-of-using-jwt-work-on-the-frontend?utm_source=chatgpt.com">https://stackoverflow.com/questions/65351531/what-is-the-flow-of-using-jwt-work-on-the-frontend</a></p></figcaption></figure>

### Étapes

#### 1️⃣ Login

Le front envoie :

```
{
  "email": "test@test.fr",
  "password": "secret"
}
```

#### 2️⃣ Symfony vérifie

Si OK :

Symfony renvoie :

```
{
  "token": "eyJhbGciOiJIUzI1NiIsInR..."
}
```

#### 3️⃣ Vue stocke le token

Dans :

* localStorage\
  ou
* sessionStorage

#### 4️⃣ Chaque appel API ajoute :

```
Authorization: Bearer eyJhbGciOiJIUzI1Ni...
```

#### 5️⃣ Symfony vérifie le token

Si valide :

→ accès autorisé

***

## 3. Anatomie d’un JWT

Un JWT contient 3 parties :

```
HEADER.PAYLOAD.SIGNATURE
```

### Exemple

```javascript
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30
```

### Header

```
{
  "alg": "HS256",
  "typ": "JWT"
}
```

### Payload

```
{
  "username": "admin@test.fr",
  "exp": 1710000000
}
```

### Signature

Permet de vérifier que le token n’a pas été modifié.

⚠️ Important :

Le payload est lisible.

👉 On ne met jamais :

❌ mot de passe\
❌ données sensibles

## 4. Installation côté Symfony

***

### Bundle recommandé

[LexikJWTAuthenticationBundle](https://github.com/lexik/LexikJWTAuthenticationBundle)

### Installation

```
composer require lexik/jwt-authentication-bundle
```

### Génération des clés

```
php bin/console lexik:jwt:generate-keypair
```

Cela crée :

```
config/jwt/private.pem
config/jwt/public.pem
```

### Configuration .env (normalement déjà fait)

```dotenv
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=monPassphrase
```

## 5. Configuration security.yaml

### Partie principale (a adapter avec votre entité)

{% hint style="info" %}
Faire make:user avant
{% endhint %}

```yaml
security:
    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'

    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
```

{% hint style="warning" icon="triangle-exclamation" %}
Adpater email pour correspondre à votre configuration symfony
{% endhint %}

### Firewall login

```yaml
firewalls:
    login:
        pattern: ^/api/login
        stateless: true
        json_login:
            check_path: /api/login
            username_path: email
            password_path: password
            success_handler: lexik_jwt_authentication.handler.authentication_success
            failure_handler: lexik_jwt_authentication.handler.authentication_failure
```

{% hint style="warning" icon="triangle-exclamation" %}
Adapter email et password qui sont les noms de vos champs venant du front
{% endhint %}

### Firewall API

```yaml
    api:
        pattern: ^/api
        stateless: true
        jwt: ~
```

### Access control (exemple à adapter)

```yaml
access_control:
    - { path: ^/api/login, roles: PUBLIC_ACCESS }
    - { path: ^/api, roles: ROLE_USER }
```

## 6. Installation côté Vue.js

{% hint style="info" %}
Exemple avec Axios, la logique est identique avec fetch. Choisissez l'un ou l'autre.
{% endhint %}

### Axios

Axios

```
npm install axios
```

## 7. Service API propre

### api.js

{% hint style="info" %}
Le service va éviter de penser a token à chaque appel.&#x20;
{% endhint %}

```js
import axios from 'axios'

const api = axios.create({
  baseURL: 'http://localhost:8000/api'
})

export default api
```

## 8. Login Vue.js

### Exemple Composition API

{% hint style="info" %}
Ici api fait référence au fichier api.js écrit précédemment
{% endhint %}

```js
import api from './api'

const login = async () => {
  const response = await api.post('/login', {
    email: email.value,
    password: password.value
  })

  localStorage.setItem('token', response.data.token)
}
```

## 9. Ajouter automatiquement le token

### Interceptor Axios (à mettre dans api.js)

```js
api.interceptors.request.use(config => {
  const token = localStorage.getItem('token')

  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }

  return config
})
```

## 10. Exemple appel protégé

```js
const response = await api.get('/users/me')
```

Symfony reçoit :

```
Authorization: Bearer xxxx
```

## 12. Récupérer l’utilisateur connecté côté Symfony

***

```php
#[Route('/api/me')]
public function me(): JsonResponse
{
    return $this->json($this->getUser());
}
```

## 13. Gestion expiration token

JWT contient :

```
exp
```

### Si expiré :

Symfony renvoie :

```
401 Unauthorized
```

### Côté Vue :

```js
if (error.response.status === 401) {
  localStorage.removeItem('token')
}
```

## 14. Déconnexion

JWT ne se "détruit" pas côté serveur.

On fait :

```js
localStorage.removeItem('token')
```

## 15. Architecture recommandée

***

```
src/
 ├── services/
 │    ├── api.js
 │    ├── auth.js
 │

```

## 16. Route guard Vue Router

Vue Router

```javascript
router.beforeEach((to, from, next) => {
  const token = localStorage.getItem('token')

  if (to.meta.requiresAuth && !token) {
    next('/login')
  } else {
    next()
  }
})
```

## Le fichier api.js complet

```js
import axios from 'axios'

const api = axios.create({
  baseURL: 'http://localhost:8000/api'
})

api.interceptors.request.use(config => {
  const token = localStorage.getItem('token')

  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }

  return config
})

export default api
```
