Open Site Pass
Une norme ouverte de code QR pour s'enregistrer sur les sites de travail. Toute application peut afficher un pass, et toute borne peut le lire.
Version 1, novembre 2026. Utilisation libre sous CC BY 4.0.
Les prestataires s'enregistrent chaque semaine sur un site différent, et chacun demande les mêmes informations : nom, entreprise, mobile, véhicule, carte Site Safe. Open Site Pass leur permet de conserver ces informations dans une application et de les transmettre en montrant un code QR. Toute application peut afficher un pass et toute borne d'enregistrement peut le lire, quel qu'en soit l'éditeur.
Avant de rédiger ceci, nous avons cherché une norme existante. Les produits d'enregistrement des visiteurs avec pass QR ont chacun leur format, qui ne fonctionne que dans ce produit. Les normes d'identité générales (W3C Verifiable Credentials, OpenID for Verifiable Presentations, permis de conduire mobiles ISO 18013-5) exigent une application portefeuille et un échange en plusieurs étapes, ce qui dépasse les besoins d'une tablette à l'entrée d'un site. C'est donc un petit format que tout langage peut lire et vérifier avec sa cryptographie intégrée.
1. Ce que contient le code QR
Le code QR contient du texte brut en trois parties reliées par des points :
OSP1.<pass>.<signature> OSP1indique qu'il s'agit d'un Open Site Pass, version 1.<pass>est le pass en JSON, UTF-8, puis base64url sans remplissage.<signature>est une signature ECDSA P-256 avec SHA-256 sur le texte ASCII de<pass>(exactement tel qu'il apparaît dans le code QR), sous la forme r ‖ s de 64 octets, puis base64url sans remplissage. C'est la forme produite parcrypto.subtle.signde Web Crypto.
2. Le pass
{
"version": 1,
"issuer": "https://timeclock.kiwi",
"keyId": "2026-11-3fa9c1",
"holderId": "b7f3c2e1a94d0c55e1f3b2a8",
"issuedAt": "2026-11-01T09:42:00.000Z",
"expiresAt": "2026-11-01T09:43:00.000Z",
"firstName": "Sam",
"lastName": "Taylor",
"company": "Harbour Electrical Ltd",
"phone": "+64215550142",
"vehicleRegistration": "QKT482",
"documents": [
{ "type": "site-safe-passport", "number": "SS-482913", "expiresOn": "2027-03-31" }
]
} | version | Toujours 1 pour cette version. |
|---|---|
| issuer | L'origine HTTPS de celui qui a signé le pass, p. ex. https://timeclock.kiwi. Ses clés sont trouvées à partir de celle-ci. |
| keyId | Quelle clé de l'émetteur l'a signé. |
| holderId | Identique pour une personne auprès d'un émetteur, et différent avec tout autre émetteur. Permet à un site de reconnaître un visiteur qui revient. Ne doit pas être un numéro d'employé, un e-mail ou quoi que ce soit qui l'identifie ailleurs. |
| issuedAt, expiresAt | Heures ISO 8601. Un pass doit durer environ une minute et pas plus de 15 minutes, pour qu'une photo devienne vite inutile. |
| firstName, lastName | Son nom. |
| company | Pour qui la personne travaille, si elle a choisi de le partager. |
| phone | Numéro de mobile, idéalement au format international. |
| Adresse e-mail. | |
| vehicleRegistration | Plaque d'immatriculation du véhicule utilisé. |
| documents | Cartes et certificats : chacun a un type (de la liste ci-dessous) et, en option, number, issuer et expiresOn (AAAA-MM-JJ). |
Obligatoires : version, issuer, keyId, holderId, issuedAt, expiresAt, firstName. Tout le reste est facultatif et choisi par le titulaire à chaque fois.
Types de documents
site-safe-passport | Site Safe passport |
|---|---|
public-liability-insurance | Assurance responsabilité civile |
electrical-registration | Enregistrement d'électricien |
plumbing-gasfitting-registration | Enregistrement de plomberie, gaz ou drainage |
forklift-licence | Permis de chariot élévateur (F endorsement) |
first-aid-certificate | Certificat de premiers secours |
working-at-heights | Travail en hauteur |
confined-spaces | Espaces confinés |
construction-induction-card | Carte d'initiation à la construction (White Card) |
cscs-card | CSCS card |
Pour proposer un autre type, contactez-nous ci-dessous. Les bornes conservent les documents de types inconnus, mais ne les associent à rien.
3. Clés de l'émetteur
Les émetteurs publient leurs clés publiques à <issuer>/.well-known/open-site-pass/keys.json :
{
"keys": [
{
"keyId": "2026-11-3fa9c1",
"publicKey": { "kty": "EC", "crv": "P-256", "x": "…", "y": "…" }
}
]
} Chaque publicKey est un JWK, que Web Crypto peut charger directement. Continuez à publier une clé au moins un jour après avoir cessé de signer avec elle. Les clés de Timeclock.Kiwi sont à https://timeclock.kiwi/.well-known/open-site-pass/keys.json.
4. Règles pour les bornes
- Vérifiez la signature avec la clé de l'émetteur et que le pass n'a pas expiré. Prévoyez jusqu'à 2 minutes de marge pour les écarts d'horloge.
- Ne récupérez les clés que depuis l'origine HTTPS de l'émetteur, et mettez-les en cache pour vérifier un pass hors ligne.
- Affichez les informations et le domaine de l'émetteur, et laissez le visiteur confirmer avant tout enregistrement.
- Considérez les informations des documents comme saisies par le titulaire, non vérifiées. Si votre site exige une vérification, faites examiner la vraie carte par une personne.
- Vous pouvez n'accepter que les pass d'émetteurs de confiance, ou accepter tout émetteur HTTPS et afficher « Verified by <domain> ».
- Ne demandez jamais à l'application du titulaire plus que ce que contient le pass. Le titulaire choisit ce qu'il partage.
5. Règles pour les applications
- Créez un nouveau pass au moins chaque minute tant qu'il est à l'écran.
- Laissez le titulaire choisir les informations facultatives, à chaque fois ou via un choix enregistré modifiable.
- N'incluez jamais ce que le titulaire n'a pas choisi, y compris son employeur, sa position ou des identifiants internes.
6. Vérifier un pass en JavaScript
C'est tout ce dont une borne a besoin, avec la seule cryptographie intégrée au navigateur :
async function readPass(text, fetchKeys) {
const [prefix, body, signature] = text.split('.');
if (prefix !== 'OSP1') throw new Error('Not a site pass');
const pass = JSON.parse(atob(body.replace(/-/g, '+').replace(/_/g, '/')));
const keys = await fetchKeys(pass.issuer); // GET <issuer>/.well-known/open-site-pass/keys.json
const jwk = keys.find(k => k.keyId === pass.keyId)?.publicKey;
const key = await crypto.subtle.importKey('jwk', jwk, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify']);
const fromB64 = s => Uint8Array.from(atob(s.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
const ok = await crypto.subtle.verify({ name: 'ECDSA', hash: 'SHA-256' }, key, fromB64(signature), new TextEncoder().encode(body));
const now = Date.now(), slack = 2 * 60 * 1000;
if (!ok || Date.parse(pass.expiresAt) + slack < now) throw new Error('Not valid');
return pass;
} L'utiliser avec Timeclock.Kiwi
Toute personne connectée à l'application Timeclock.Kiwi peut afficher un pass depuis Mon pass de site, et chaque borne d'enregistrement Timeclock.Kiwi les lit, y compris les pass d'autres applications qui suivent ce format.