/// Connexion native Google (Credential Manager) pour l'app cliente. /// /// L'app obtient un « ID token » Google via le sélecteur de compte natif, puis /// l'envoie ici. On le vérifie auprès de Google, on retrouve (ou on crée) le /// compte `users`, on s'assure qu'une fiche fidélité `clients` existe (créée /// automatiquement à partir du nom/prénom Google), et on renvoie une session. /// /// Endpoint : POST /api/google-native-auth body: { "idToken": "..." } /// Réponse : { "token": "", "record": { ...compte... } } routerAdd("POST", "/api/google-native-auth", (e) => { // ID client OAuth « Web » (celui passé comme serverClientId côté app). const WEB_CLIENT_ID = "831733439319-45cergb7cf8r728fja4m0kcvu2r7fbj9.apps.googleusercontent.com"; const body = new DynamicModel({ idToken: "" }); e.bindBody(body); const idToken = (body.idToken || "").trim(); if (!idToken) { throw new BadRequestError("idToken manquant"); } // Vérification de l'ID token auprès de Google (signature + expiration). const res = $http.send({ url: "https://oauth2.googleapis.com/tokeninfo?id_token=" + encodeURIComponent(idToken), method: "GET", timeout: 15, }); if (res.statusCode !== 200) { throw new BadRequestError("ID token Google invalide"); } const p = res.json; if (p.aud !== WEB_CLIENT_ID) { throw new BadRequestError("audience inattendue"); } if ( p.iss !== "accounts.google.com" && p.iss !== "https://accounts.google.com" ) { throw new BadRequestError("issuer inattendu"); } const email = (p.email || "").toLowerCase(); if (!email) { throw new BadRequestError("email absent du token"); } if (String(p.email_verified) !== "true") { throw new BadRequestError("email Google non verifie"); } // Compte existant ? let user = null; try { user = $app.findFirstRecordByFilter("users", "email = {:email}", { email: email, }); } catch (_) { user = null; } // Sinon création d'un compte client (is_staff = false). if (!user) { const collection = $app.findCollectionByNameOrId("users"); user = new Record(collection); user.set("email", email); user.set("emailVisibility", false); user.set("verified", true); user.set("name", p.name || ""); user.set("is_staff", false); user.setPassword($security.randomString(30)); $app.save(user); } // Fiche fidélité déjà présente ? Sinon on la crée à partir du nom/prénom // Google → l'app cliente arrive directement sur sa carte (pas de saisie). let hasProfile = false; try { $app.findFirstRecordByFilter("clients", "user = {:uid}", { uid: user.id }); hasProfile = true; } catch (_) { hasProfile = false; } if (!hasProfile) { const prenom = p.given_name || ""; let nom = p.family_name || ""; if (!nom) { nom = p.name || prenom || email.split("@")[0]; } const code = "FID-" + $security.randomStringWithAlphabet(6, "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"); const clientsCol = $app.findCollectionByNameOrId("clients"); const client = new Record(clientsCol); client.set("user", user.id); client.set("nom", nom); client.set("prenom", prenom); client.set("code", code); $app.save(client); } const token = user.newAuthToken(); return e.json(200, { token: token, record: user.publicExport() }); });