Geolocation API avec Claude Code : permissions, confidentialité, fallback et tests
Implémentez Geolocation API avec Claude Code : permissions, HTTPS, saisie manuelle, confidentialité et tests.
La géolocalisation paraît simple tant qu’elle reste une démonstration. En production, un bouton “utiliser ma position” implique une demande de permission, un contexte sécurisé, des échecs réseau ou système, une alternative manuelle, une frontière avec les SDK de carte et une règle claire sur les journaux.
Claude Code est utile pour accélérer ce travail, mais seulement si la demande est cadrée. “Ajoute la position actuelle” peut produire un appel à getCurrentPosition sans expliquer la permission, sans fallback et avec des coordonnées brutes dans les logs. Une version publiable doit respecter le choix de l’utilisateur et rester utile même lorsque la localisation échoue.
Les sources primaires utilisées ici sont MDN Geolocation API, getCurrentPosition, watchPosition, Permissions API, la spécification W3C Geolocation, l’exigence Chrome de secure origins, Chrome DevTools Sensors, Playwright emulation et Claude Code permissions. Pour compléter, consultez l’intégration de cartes, l’audit de sécurité et le responsive design.
Décider avant d’implémenter
Geolocation API demande au navigateur d’estimer la position de l’appareil. Le navigateur peut utiliser GPS, Wi-Fi, antennes mobiles, IP, services du système ou cache. L’application ne choisit pas la source et ne doit pas promettre une précision absolue.
Quatre décisions évitent la plupart des bugs. Demandez la position après une action explicite, pas au chargement de la page. Gardez enableHighAccuracy: false par défaut, sauf besoin métier réel. Ajoutez une saisie d’adresse, de ville ou de code postal. Enfin, décidez ce qui est conservé : souvent, le résultat métier suffit et les coordonnées exactes ne doivent pas être stockées.
| Décision | Défaut recommandé | Échec courant |
|---|---|---|
| Moment du prompt | Après clic utilisateur | Prompt immédiat au chargement |
| Précision | Haute précision désactivée | GPS demandé pour une simple liste |
| Fallback | Adresse ou code postal | Flux bloqué après refus |
| Logs | Événements et zones grossières | Coordonnées brutes dans analytics |
La limite avec la carte doit aussi être claire. Geolocation donne des coordonnées. L’affichage d’une carte, le géocodage, les itinéraires et la recherche de lieux appartiennent à Google Maps, Mapbox, OpenStreetMap ou à votre backend.
Cas d’usage concrets
Premier cas : la recherche de magasin. L’utilisateur clique sur “utiliser ma position”, l’application trie les magasins proches, puis affiche un rayon de recherche. Le champ “code postal ou ville” doit être visible au même niveau que le bouton pour ne pas perdre les utilisateurs qui refusent.
Deuxième cas : disponibilité de livraison ou de service à domicile. Une application peut vérifier si l’adresse est dans une zone servie et proposer un créneau. Il est souvent suffisant de garder inside_zone, une tranche de distance ou le magasin affecté.
Troisième cas : check-in terrain. Maintenance, nettoyage, événementiel ou vente peuvent vérifier qu’une personne est proche du site prévu. Mais un échec de localisation ne doit pas bloquer le travail : photo, validation du responsable ou note manuelle sont des alternatives nécessaires.
Quatrième cas : contenu local. Météo, événements, stock magasin ou alertes locales peuvent dépendre de la zone. Si la ville suffit, une préférence enregistrée est souvent plus respectueuse qu’une position précise.
Exemple getCurrentPosition prêt à copier
Enregistrez ce fichier sous geo-demo.html et servez-le via localhost ou HTTPS. Il couvre permission, timeout, cache, arrondi et saisie manuelle.
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Geolocation demo</title>
<style>
body {
font-family: system-ui, sans-serif;
line-height: 1.6;
margin: 2rem;
}
button,
input {
font: inherit;
padding: 0.7rem 0.9rem;
}
.panel {
border: 1px solid #ddd;
max-width: 36rem;
padding: 1rem;
}
</style>
</head>
<body>
<main class="panel">
<h1>Trouver des magasins proches</h1>
<p>
Nous utilisons votre position uniquement pour trier les magasins.
Nous ne stockons pas votre adresse exacte.
</p>
<button id="useLocation" type="button">Utiliser ma position</button>
<p id="status" role="status" aria-live="polite"></p>
<pre id="result"></pre>
<form id="manualForm">
<label for="postcode">Code postal ou ville</label>
<input id="postcode" name="postcode" autocomplete="postal-code" />
<button type="submit">Rechercher manuellement</button>
</form>
</main>
<script type="module">
const status = document.querySelector("#status");
const result = document.querySelector("#result");
const button = document.querySelector("#useLocation");
const form = document.querySelector("#manualForm");
function showManual(reason) {
status.textContent =
`${reason}. Vous pouvez aussi chercher par code postal.`;
}
function onSuccess(position) {
const { latitude, longitude, accuracy } = position.coords;
status.textContent = "Position reçue.";
result.textContent = JSON.stringify(
{
lat: Number(latitude.toFixed(5)),
lng: Number(longitude.toFixed(5)),
accuracyMeters: Math.round(accuracy),
},
null,
2,
);
}
function onError(error) {
const messages = {
1: "La permission de localisation a été refusée",
2: "La position de l'appareil est indisponible",
3: "La demande de localisation a expiré",
};
showManual(messages[error.code] ?? "Position indisponible");
}
button.addEventListener("click", () => {
if (!("geolocation" in navigator)) {
showManual("Ce navigateur ne prend pas en charge Geolocation");
return;
}
status.textContent = "Vérification de la permission...";
navigator.geolocation.getCurrentPosition(onSuccess, onError, {
enableHighAccuracy: false,
timeout: 8000,
maximumAge: 60000,
});
});
form.addEventListener("submit", (event) => {
event.preventDefault();
const data = new FormData(form);
status.textContent =
`Recherche autour de "${data.get("postcode")}".`;
});
</script>
</body>
</html>
timeout fixe le temps d’attente maximal. maximumAge accepte une position en cache assez récente. enableHighAccuracy demande une meilleure précision quand c’est possible, avec un coût potentiel en temps et batterie.
Utiliser watchPosition sans fuite
watchPosition reçoit des mises à jour continues. Il convient aux trajets et opérations terrain, pas à une recherche ponctuelle. L’identifiant retourné doit être nettoyé avec clearWatch.
import { useEffect, useRef, useState } from "react";
type LocationPoint = {
lat: number;
lng: number;
accuracy: number;
at: string;
};
export function TrackingPanel() {
const watchId = useRef<number | null>(null);
const [points, setPoints] = useState<LocationPoint[]>([]);
const [error, setError] = useState<string | null>(null);
function start() {
if (!navigator.geolocation || watchId.current !== null) return;
watchId.current = navigator.geolocation.watchPosition(
(position) => {
const { latitude, longitude, accuracy } = position.coords;
setPoints((current) => [
{
lat: Number(latitude.toFixed(5)),
lng: Number(longitude.toFixed(5)),
accuracy: Math.round(accuracy),
at: new Date(position.timestamp).toISOString(),
},
...current.slice(0, 9),
]);
},
(err) => setError(`Suivi impossible : ${err.code}`),
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 5000,
},
);
}
function stop() {
if (watchId.current === null) return;
navigator.geolocation.clearWatch(watchId.current);
watchId.current = null;
}
useEffect(() => stop, []);
return (
<section>
<button type="button" onClick={start}>Démarrer le suivi</button>
<button type="button" onClick={stop}>Arrêter</button>
{error && <p role="alert">{error}</p>}
<ol>
{points.map((point) => (
<li key={point.at}>
{point.lat}, {point.lng}
{" / "}
{point.accuracy}m
</li>
))}
</ol>
</section>
);
}
Le suivi doit être visible dans le produit. “Démarrer”, “mettre en pause” et “terminer” sont des états de confiance. Définissez aussi durée de conservation, accès interne et suppression.
Confidentialité et journalisation
Les coordonnées ne sont pas de simples logs. Elles peuvent finir dans l’observabilité, l’analytics, les captures de support ou les replays. Préférez un résultat d’événement ou des zones grossières.
type GeoLogInput = {
lat: number;
lng: number;
accuracy: number;
permission: "granted" | "prompt" | "denied" | "unknown";
};
export function toPrivacySafeGeoLog(input: GeoLogInput) {
return {
permission: input.permission,
accuracyBucket:
input.accuracy <= 50 ? "high" :
input.accuracy <= 500 ? "medium" : "low",
latBucket: Number(input.lat.toFixed(2)),
lngBucket: Number(input.lng.toFixed(2)),
};
}
Beaucoup d’analyses n’ont besoin que de permission_denied, manual_search_used, timeout ou results_shown. Demandez explicitement à Claude Code de ne jamais logger latitude et longitude bruts.
La Permissions API peut aider à afficher un message adapté, mais elle ne remplace pas le flux d’autorisation du navigateur.
export async function readGeoPermission() {
if (!("permissions" in navigator)) return "unknown";
try {
const status = await navigator.permissions.query({
name: "geolocation",
});
return status.state;
} catch {
return "unknown";
}
}
Échecs à tester
Testez HTTP, HTTPS et iframe. Les navigateurs modernes exigent un contexte sécurisé, et Permissions-Policy peut bloquer la localisation dans un iframe tiers.
Testez le refus de permission. L’interface doit passer à la saisie manuelle sans culpabiliser l’utilisateur.
Testez timeout et position indisponible. Ordinateur fixe, VPN, intérieur, service système désactivé ou navigateur géré par l’entreprise peuvent échouer.
Testez le cache. maximumAge rend l’expérience plus rapide, mais peut donner une ancienne position. Pour un check-in, choisissez un cache court.
import { expect, test } from "@playwright/test";
test.use({
geolocation: {
latitude: 48.856613,
longitude: 2.352222,
accuracy: 50,
},
permissions: ["geolocation"],
});
test("shows nearby stores from mocked location", async ({ page }) => {
await page.goto("/stores");
await page.getByRole("button", { name: "Utiliser ma position" }).click();
await expect(page.getByText("Position reçue")).toBeVisible();
});
Prompt Claude Code sécurisé
Le prompt doit préciser les fichiers, les interdictions et la preuve attendue.
claude <<'PROMPT'
Implement a beginner-friendly Geolocation feature.
Scope:
- Edit only src/features/location and related tests.
- Do not change billing, analytics, or map provider config.
- Preserve existing API keys and environment variable names.
Requirements:
- Request location only after the user clicks a button.
- Explain why location is needed before the browser prompt.
- Use getCurrentPosition with timeout and maximumAge.
- Add manual postcode/address fallback for denied or timeout cases.
- Do not log raw latitude or longitude.
- Add a Playwright test with mocked geolocation.
- Return a short verification checklist.
PROMPT
Pour une équipe, limitez aussi les permissions de Claude Code.
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Bash(git push *)"
],
"allow": [
"Bash(npm test *)",
"Bash(npm run lint)"
]
}
}
Si vous voulez transformer cela en standard d’équipe, revue de code ou prompt adapté à votre produit, la page Claude Code training and consultation est le point d’entrée le plus utile.
Note de vérification
J’ai vérifié les exemples dans Chrome sur localhost, avec DevTools Sensors sur Paris et “Location unavailable”, puis avec Playwright en permission accordée. Avant production, ajoutez les cas de refus, service de localisation désactivé, iframe tiers, quota du fournisseur de carte et recherche de coordonnées brutes dans les logs.
PDF gratuit: cheatsheet Claude Code
Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.
Nous protégeons vos données et n'envoyons pas de spam.
À propos de l'auteur
Masa
Ingénieur spécialisé dans les workflows pratiques avec Claude Code.
Articles liés
Workflow Obsidian vers CLAUDE.md avec Claude Code
Transformer des notes Obsidian en notes CLAUDE.md concises pour reprendre les sessions sans réexpliquer.
Claude Code Revenue CTA Routing : relier articles, PDF, Gumroad et consultation
Un workflow Claude Code pour orienter les lecteurs vers PDF gratuit, Gumroad ou consultation selon l'intention.
Règles de handoff Claude Code en équipe: preuves, permissions, rollback et revenus
Un format concret pour transmettre un travail Claude Code avec preuves, permissions, rollback, PDF gratuit, Gumroad et consultation.