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

StatutCodeSignification
401missing_keyPas d’en-tête Authorization: Bearer. Une clé dans l’URL ne compte pas.
401invalid_keyLa clé ne correspond à aucun compte. Vérifiez qu’il n’y a pas de saut de ligne ou de guillemet en trop.
403plan_requiredLa 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.

QuoiOù
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’autorisationhttps://publicwww.com/oauth/authorize
Point de terminaison de jetonhttps://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_id indiqué 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_uris sont des adresses https, ou http sur 127.0.0.1, localhost ou [::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 comme myapp:// 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_method indiqué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ùCodeSignification
Autorisationpage d’erreurLe 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.
Autorisationinvalid_requestPas de code_challenge, ou une méthode autre que S256.
Autorisationunsupported_response_typeToute valeur autre que response_type=code.
Autorisation, jetoninvalid_targetUne resource autre que l’API.
Autorisationaccess_deniedLa personne a cliqué sur Annuler.
Jetoninvalid_grantLe code est inconnu, déjà utilisé, expiré ou émis pour un autre client_id ; ou bien code_verifier ou redirect_uri ne correspond pas.
Jetonunsupported_grant_typeToute 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.

Suivant Envoyer des requêtes