Authentification
Un seul en-tête, sur chaque requête, sauf pour l’index autodescriptif.
Authorization: Bearer <your api key>
Les jetons se créent sur votre page de profil - jusqu’à dix
par compte, chacun révocable individuellement : un jeton divulgué peut ainsi être
supprimé sans toucher aux autres. Un forfait payant est requis : sans forfait,
tous les points de terminaison sauf / et /v1/account répondent
403 plan_required.
Une application peut aussi obtenir un jeton pour vous via OAuth 2.1 : vous vous connectez, vous voyez ce qu’elle demande et vous cliquez sur Autoriser. Son jeton se place dans le même en-tête et fonctionne de la même manière.
Pourquoi pas ?key=
Une clé placée dans l’URL finit là où vous ne l’avez pas mise : journaux d’accès
du serveur web, historique du navigateur, journaux des proxys et en-tête
Referer de tout ce vers quoi la réponse pointe. L’API ne l’accepte
donc pas et répond 401 missing_key en l’expliquant.
Les anciennes URL ?export= du site principal acceptent toujours
?key=, car des scripts écrits il y a des années en dépendent et
le supprimer les casserait. C’est le seul endroit où il subsiste - voir
les anciennes URL d’export.
Vérifier qu’une clé fonctionne
/v1/account est l’appel le moins coûteux : il ne consomme aucun quota
et fonctionne même sur un compte sans forfait. Il répond donc à la fois à
« cette clé est-elle valide ? » et à « à quoi ai-je droit ? ».
curl -H "Authorization: Bearer $KEY" https://api.publicwww.com/v1/account
{
"plan": "enterprise",
"plan_until": 1819461840,
"full_access": true,
"quota": {
"searches": { "limit": 300, "used": 12, "resets_at": 1787961600 },
"snippets": { "limit": 100, "used": 3, "resets_at": 1787961600 }
},
"limits": {
"disclosed_positions": 4294967295,
"max_per_page": 1000000,
"max_per_page_snippets": 10000
}
}
Ce qui peut mal tourner
| Statut | Code | Signification |
|---|---|---|
| 401 | missing_key | Pas d’en-tête Authorization: Bearer. Une clé dans l’URL ne compte pas. |
| 401 | invalid_key | La clé ne correspond à aucun compte. Vérifiez qu’il n’y a pas de saut de ligne ou de guillemet en trop. |
| 403 | plan_required | La clé est valide, mais le compte n’a pas de forfait payant. |
Une réponse 401 comporte aussi un en-tête WWW-Authenticate: Bearer :
les clients HTTP qui gèrent l’authentification de manière générique se comportent
donc correctement.
OAuth 2.1 pour les applications
Une application qui agit pour le compte d’autres personnes - un assistant, une
intégration, un service hébergé - ne devrait pas demander à chacune de copier un
jeton. Elle les envoie plutôt sur PublicWWW : elles se connectent, approuvent
l’application, et celle-ci reçoit son propre jeton. Ce jeton s’envoie comme
n’importe quel autre dans Authorization: Bearer et donne accès à toute l’API
ainsi qu’au serveur MCP à l’adresse https://api.publicwww.com/mcp,
dans le cadre du forfait, du quota et de la limite de débit du compte.
| Quoi | Où |
|---|---|
| Métadonnées du serveur d’autorisation (RFC 8414) | https://publicwww.com/.well-known/oauth-authorization-server |
| Métadonnées de la ressource protégée (RFC 9728) | https://api.publicwww.com/.well-known/oauth-protected-resource |
| Point de terminaison d’autorisation | https://publicwww.com/oauth/authorize |
| Point de terminaison de jeton | https://publicwww.com/oauth/token |
| Point de terminaison de révocation (RFC 7009) | https://publicwww.com/oauth/revoke |
Identification de l’application
Il n’y a pas d’enregistrement de client. Le client_id est l’URL https
d’un petit document JSON que l’application publie : un document de métadonnées
du client. PublicWWW le lit à chaque connexion, si bien que le nom et les
adresses de retour sont toujours à jour, et la personne qui donne son accord voit
quel hôte les a publiés.
{
"client_id": "https://app.example.com/oauth/client.json",
"client_name": "Example App",
"redirect_uris": ["https://app.example.com/oauth/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}
-
Le
client_idindiqué dans le document doit être exactement l’URL depuis laquelle il est servi. Le document est récupéré en https, depuis une URL comportant un chemin, sans suivre les redirections ; il doit répondre en moins de 5 secondes et peser moins de 64 Ko. -
Les
redirect_urissont des adresses https, ou http sur127.0.0.1,localhostou[::1]pour une application qui tourne sur l’ordinateur de la personne - dans ce cas, n’importe quel port convient. Les schémas personnalisés commemyapp://ne sont pas acceptés. -
Toute application est un client public : la demande de jeton ne contient
aucun secret, quelle que soit la
token_endpoint_auth_methodindiquée dans le document. Le code d’autorisation est protégé par PKCE à la place.
Le flux
Code d’autorisation avec PKCE ; S256 est la seule méthode. Envoyez
la personne vers le point de terminaison d’autorisation :
https://publicwww.com/oauth/authorize
?response_type=code
&client_id=https%3A%2F%2Fapp.example.com%2Foauth%2Fclient.json
&redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback
&code_challenge=<BASE64URL(SHA-256(code_verifier))>
&code_challenge_method=S256
&state=<random>
Elle se connecte avec un code à usage unique envoyé par e-mail si ce n’est pas
déjà fait, voit le nom de l’application, l’hôte de son document et l’adresse de
retour, puis clique sur Autoriser ou Annuler. Sur redirect_uri
reviennent code, votre state et
iss=https://publicwww.com (RFC 9207). Un code est valable dix
minutes et ne sert qu’une fois. Échangez-le :
curl https://publicwww.com/oauth/token \
-d grant_type=authorization_code \
-d code="$CODE" \
-d code_verifier="$VERIFIER" \
-d client_id=https://app.example.com/oauth/client.json \
-d redirect_uri=https://app.example.com/oauth/callback
{ "access_token": "<token>", "token_type": "Bearer", "scope": "mcp" }
scope peut être omis : il n’existe qu’une portée, mcp, et
elle couvre toute l’API. resource (RFC 8707) peut aussi être omis ;
s’il est envoyé, il vaut https://api.publicwww.com/mcp ou
https://api.publicwww.com.
Durée de vie d’un jeton
Jusqu’à sa révocation - il n’y a ni expiration ni jeton d’actualisation. Une intégration qui fonctionne aujourd’hui fonctionnera encore demain sans que personne n’y touche. Un jeton n’est révoqué que volontairement : la personne déconnecte l’application sur sa page de profil, l’application le révoque elle-même, ou le compte est supprimé.
curl https://publicwww.com/oauth/revoke \
-d token="$TOKEN" \
-d client_id=https://app.example.com/oauth/client.json
Le point de terminaison de révocation répond toujours 200, que le
jeton ait existé ou non.
Erreurs OAuth
| Où | Code | Signification |
|---|---|---|
| Autorisation | page d’erreur | Le document client_id n’a pas pu être lu, ou redirect_uri n’y figure pas. La personne n’est pas renvoyée : une adresse non vérifiée n’est jamais suivie. |
| Autorisation | invalid_request | Pas de code_challenge, ou une méthode autre que S256. |
| Autorisation | unsupported_response_type | Toute valeur autre que response_type=code. |
| Autorisation, jeton | invalid_target | Une resource autre que l’API. |
| Autorisation | access_denied | La personne a cliqué sur Annuler. |
| Jeton | invalid_grant | Le code est inconnu, déjà utilisé, expiré ou émis pour un autre client_id ; ou bien code_verifier ou redirect_uri ne correspond pas. |
| Jeton | unsupported_grant_type | Toute valeur autre que authorization_code. |
Les erreurs d’autorisation autres que la page d’erreur reviennent sur
redirect_uri sous forme de error, error_description,
state et iss ; les erreurs de jeton sont une réponse 400
avec ces deux mêmes champs en JSON.