Connexion dans le navigateur
Ouvrir votre page de connexion dans le navigateur du téléphone (passkeys, trousseau).
Pourquoi passer par le navigateur
Section intitulée « Pourquoi passer par le navigateur »Dans une vue web intégrée à une application, iOS et Android n’autorisent les passkeys (WebAuthn, Face ID, empreinte digitale) et le remplissage automatique des mots de passe que pour les domaines explicitement associés à l’application. Votre domaine ne l’est pas. Résultat, dans l’écran du lien :
- le bouton de connexion biométrique de votre site n’apparaît pas ou échoue ;
- le trousseau propose les identifiants de l’application de la commune, pas ceux de votre site.
La fonction « Connexion dans le navigateur » contourne cette limite sans rien déclarer dans l’application : votre page de connexion s’ouvre dans le navigateur du système, par-dessus l’application, où tout fonctionne comme d’habitude. Une fois l’utilisateur connecté, votre page redirige vers une URL de rappel, la feuille se ferme et votre page dans l’application reprend la main.
| Plateforme | Navigateur utilisé |
|---|---|
| iPhone | Feuille Safari (ASWebAuthenticationSession), qui partage les cookies, passkeys et mots de passe de Safari. |
| Android | Onglet du navigateur par défaut (Auth Tab, ou Custom Tab si le navigateur ne connaît pas l’Auth Tab), qui partage les cookies et identifiants de ce navigateur. |
Sur iPhone, le système affiche d’abord une alerte qui demande à l’utilisateur d’autoriser l’application à utiliser votre site pour se connecter. S’il refuse, open() est rejetée avec CANCELLED.
La case « Connexion dans le navigateur » doit être cochée sur la carte lien. mairies.auth n’existe que dans ce cas.
Ouvrir la page de connexion
Section intitulée « Ouvrir la page de connexion »| Propriété ou méthode | Description |
|---|---|
mairies.auth.callbackUrl |
URL de rappel propre à l’application, par exemple net.mairies.macommune.webauth://callback. Lecture seule. |
mairies.auth.open(url) |
Ouvre url dans le navigateur du système. Se résout avec { url } : l’URL de rappel complète, dès que votre page de connexion redirige vers callbackUrl. |
L’adresse passée à open() doit être en https et sur la même origine que votre page (même hôte, même port). Sinon, l’appel est rejeté avec NOT_ALLOWED. Si le paramètre manque, l’erreur est INVALID_PARAMS.
La promesse reste en attente pendant toute la connexion, qui peut durer plusieurs minutes. N’y mettez pas de délai d’expiration court.
L’URL de rappel
Section intitulée « L’URL de rappel »L’URL de rappel a la forme <identifiant de l’application>.webauth://callback. Elle diffère d’une application à l’autre : deux applications mairies.net installées sur le même téléphone ne reçoivent pas le rappel l’une de l’autre. Lisez-la toujours dans mairies.auth.callbackUrl, ne l’écrivez pas en dur.
Déroulé recommandé :
Écran du lien (votre page) Navigateur du système (votre page de connexion) Votre serveur────────────────────────── ─────────────────────────────────────────────── ─────────────state = aléatoire, mémorisémairies.auth.open(https://vous/connexion?state=…&return=<callbackUrl>) l’utilisateur se connecte (passkey, trousseau…) ─── demande un code à usage unique ────────────▶ émet le code (≤ 60 s) location.replace(<callbackUrl>?code=…&state=…)◀── promesse résolue : { url } ───────vérifie state, envoie le code ────────────────────────────────────────────────────────▶ échange code → sessionsession ouverte dans l’écran du lienCôté page affichée dans l’application :
async function loginWithSystemBrowser() { const m = window.mairies; if (!m?.auth) return false; // hors application ou case non cochée : formulaire habituel
const state = crypto.randomUUID(); sessionStorage.setItem('auth-state', state);
const target = new URL('/connexion-navigateur', location.origin); target.searchParams.set('state', state); target.searchParams.set('return', m.auth.callbackUrl);
try { const { url } = await m.auth.open(target.toString()); const back = new URL(url); if (back.searchParams.get('state') !== sessionStorage.getItem('auth-state')) { throw new Error('state invalide'); } await exchangeCodeForSession(back.searchParams.get('code')); // votre backend return true; } catch (error) { if (error.code === 'CANCELLED') return false; // feuille fermée par l’utilisateur throw error; } finally { sessionStorage.removeItem('auth-state'); }}Côté page de connexion, ouverte dans le navigateur, une fois l’utilisateur authentifié :
const params = new URLSearchParams(location.search);const returnUrl = params.get('return');
// N’acceptez que la forme <…>.webauth://callbackif (!/^[a-z][a-z0-9+.-]*\.webauth:\/\/callback$/.test(returnUrl ?? '')) { throw new Error('Destination de retour refusée');}
const { code } = await fetch('/api/one-time-code', { method: 'POST' }).then((r) => r.json());const callback = new URL(returnUrl);callback.searchParams.set('code', code);callback.searchParams.set('state', params.get('state'));location.replace(callback.toString());Sécuriser l’échange (code à usage unique et state)
Section intitulée « Sécuriser l’échange (code à usage unique et state) »Une URL de rappel à schéma personnalisé n’est pas réservée : sur Android notamment, une autre application installée peut tenter de se faire passer pour le rappel. Ces règles rendent une telle tentative inoffensive :
- Ne mettez jamais de jeton de session dans l’URL de rappel. Transmettez un code à usage unique, valable quelques dizaines de secondes au plus, que votre page échange ensuite contre une session auprès de votre serveur.
- Générez un
statealéatoire avantopen(), gardez-le dans la page, et vérifiez-le au retour. Un faux rappel ne le connaît pas. - Sur votre page de connexion, n’acceptez comme destination de retour que la forme
<…>.webauth://callback. Jamais une URLhttp(s)oujavascript:fournie par un paramètre : ce serait une redirection ouverte.
De son côté, l’application :
- refuse d’ouvrir une adresse d’une autre origine que celle du lien ;
- n’ouvre qu’une connexion à la fois ;
- ne renvoie l’URL de rappel qu’à la page qui a appelé
open(), et seulement si elle porte le schéma de l’application.
Annulation et session déjà ouverte
Section intitulée « Annulation et session déjà ouverte »| Situation | Résultat de open() |
|---|---|
| L’utilisateur ferme la feuille ou l’onglet sans se connecter | Rejet avec CANCELLED |
| L’utilisateur quitte l’écran du lien pendant la connexion | Rejet avec CANCELLED (si la page peut encore le recevoir) |
| Une connexion est déjà en cours | Rejet avec BUSY, rien n’est ouvert |
| Le navigateur du système ne peut pas s’ouvrir | Rejet avec UNAVAILABLE |
| Toute autre erreur du navigateur | Rejet avec INTERNAL |
Traitez CANCELLED comme un choix de l’utilisateur, pas comme une erreur : revenez simplement à l’écran de connexion. Pour éviter BUSY, désactivez votre bouton de connexion tant que la promesse est en attente.
L’utilisateur est déjà connecté dans son navigateur ? La session du navigateur est partagée (cookies conservés). Votre page de connexion peut donc reconnaître l’utilisateur et rediriger aussitôt vers l’URL de rappel ; la feuille s’ouvre et se referme presque immédiatement.