Authentification¶
Tous les endpoints protégés de l'API attendent une preuve d'identité dans l'en-tête de la requête. Elle repose toujours sur un identifiant obtenu au préalable : une session (éventuellement un jeton OpenID Connect) ou une clé d'API.
Une famille d'endpoints reste volontairement hors de tout cela : le référentiel des gares ferroviaires est une donnée ouverte rattachée à aucun groupe, et n'attend aucun identifiant.
Obtenir une session¶
1. Découvrir le provider du groupe¶
GET /api/v4/auth/provider?group_id=<group_id> indique comment un groupe s'authentifie : {"provider": "legacy"} pour un login e-mail / mot de passe, ou {"provider": "auth0", "auth0": {"domain": "...", "connection": "..."}} lorsque le groupe est délégué à un provider OpenID Connect.
const groupId = process.env.GROUP_ID
const url = new URL("https://api.pysae.com/api/v4/auth/provider")
url.searchParams.set("group_id", groupId)
const response = await fetch(url)
if (!response.ok) {
throw new Error(`Provider lookup failed: ${response.status}`)
}
// { provider: "legacy" } or { provider: "auth0", auth0: { domain: ... } }
console.log(await response.json())
2. Ouvrir la session¶
Groupe legacy — POST /api/v4/login. Corps application/x-www-form-urlencoded avec email et password. La réponse renvoie {"session_id": "...", "expires_days": ...}.
const email = process.env.EMAIL
const password = process.env.PASSWORD
const response = await fetch("https://api.pysae.com/api/v4/login", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({ email, password }),
})
if (!response.ok) {
throw new Error(`Login failed: ${response.status}`)
}
const { session_id: sessionId } = await response.json()
Groupe auth0 — jeton OpenID Connect. Obtenir un JWT auprès du provider OpenID Connect renvoyé par auth/provider, puis l'utiliser en Authorization: Bearer <jwt>. Le login e-mail / mot de passe est refusé pour ces groupes.
Le session_id (ou le JWT) obtenu s'emploie ensuite dans les requêtes protégées, cf. la section « Mécanismes acceptés ». GET /api/v4/logout ferme la session (réponse 204) :
curl --request GET \
--header "Authorization: Bearer <session_id>" \
"https://api.pysae.com/api/v4/logout"
Obtenir une clé d'API¶
Une clé d'API est un identifiant persistant et révocable, lié à un groupe et à un principal (un utilisateur, un appareil ou un rôle). Elle évite de manipuler un mot de passe dans un système automatisé.
Sa création passe par un appel API authentifié par une session : il faut donc d'abord ouvrir une session (ci-dessus) disposant du rôle admin. POST /api/v4/groups/{group_id}/api-keys crée la clé et la renvoie dans le champ value. Le corps déclare exactement un principal : user_id, device_id ou role (ce dernier accompagné de group_ids) ; scopes et expire permettent de la restreindre.
const groupId = process.env.GROUP_ID
const sessionId = process.env.SESSION_ID
const userId = process.env.USER_ID
const response = await fetch(
`https://api.pysae.com/api/v4/groups/${groupId}/api-keys`,
{
method: "POST",
headers: {
Authorization: `Bearer ${sessionId}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ user_id: userId }),
},
)
if (!response.ok) {
throw new Error(`API key creation failed: ${response.status}`)
}
const { value: apiKey } = await response.json()
Les clés d'un groupe se listent via GET /api/v4/groups/{group_id}/api-keys et se suppriment via DELETE /api/v4/groups/{group_id}/api-keys/{api_key_id}. Une clé couvrant plusieurs groupes, ou une clé superuser, se crée via POST /api/v4/api-keys.
Mécanismes acceptés¶
| Mécanisme | En-tête | Usage |
|---|---|---|
| Session en bearer | Authorization: Bearer <session_id> |
Client programmatique réutilisant une session |
| Clé d'API | Authorization: Api-Key <api_key> ou x-api-key: <api_key> |
Machine-à-machine (recommandé) |
| JWT (OpenID Connect) | Authorization: Bearer <jwt> |
Groupes délégués à un provider OpenID Connect |
En-tête Authorization¶
Les mécanismes fondés sur l'en-tête Authorization — session en bearer, clé d'API et jeton OpenID Connect — s'emploient de façon identique : on place la valeur exacte de l'en-tête dans la requête. Dans l'exemple ci-dessous, PYSAE_AUTH vaut selon le cas Bearer <session_id>, Api-Key <api_key> ou Bearer <jwt>.
const groupId = process.env.GROUP_ID
const authorization = process.env.PYSAE_AUTH
const url = new URL(
`https://api.pysae.com/api/v4/groups/${groupId}/export/trips`,
)
url.searchParams.set("date", "20260131")
const response = await fetch(url, { headers: { Authorization: authorization } })
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`)
}
La clé d'API accepte aussi l'en-tête dédié x-api-key: <api_key>, équivalent à Authorization: Api-Key <api_key> :
const groupId = process.env.GROUP_ID
const apiKey = process.env.PYSAE_API_KEY
const url = new URL(
`https://api.pysae.com/api/v4/groups/${groupId}/export/trips`,
)
url.searchParams.set("date", "20260131")
const response = await fetch(url, { headers: { "x-api-key": apiKey } })
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`)
}
Usage machine-à-machine¶
Pour une intégration automatisée :
- Clé d'API (recommandé) — persistante, révocable, scopée par groupe et par rôle, sans mot de passe dans le code appelant.
Le parcours POST /api/v4/login vise l'usage humain (interface web) et n'est pas recommandé pour l'automatisation.
Le versionnement (v2–v5, v4 par défaut) et le découpage public / internal / datahub sont décrits sur la page d'accueil.