SPB Git

spb/groupe-ka Public

Groupe KA — site du holding + KA ID (compte unique & SSO des 7 plateformes). Next.js 16, SQLite, Google & Apple login.

TypeScript 93.9% CSS 6%
2.3 KB · 77 lines typescript
Raw Blame History
1// Auteur : Simon-Pierre Boucher — contact@spboucher.ai2import crypto from "crypto";3// KA ID — registre des plateformes autorisées à déléguer leur connexion4// au hub (« Se connecter avec KA »). Chaque client a son secret partagé5// (env) et une liste blanche de redirect_uri.6export const SSO_CLIENTS: Record<7  string,8  { label: string; redirectPrefixes: string[]; secretEnv: string }9> = {10  "lou-ka": {11    label: "Lou·Ka",12    redirectPrefixes: ["https://www.lou-ka.com/", "http://localhost:8095/"],13    secretEnv: "KA_SSO_SECRET_LOU_KA",14  },15  "immo-ka": {16    label: "Immo·Ka",17    redirectPrefixes: ["https://www.immo-ka.com/"],18    secretEnv: "KA_SSO_SECRET_IMMO_KA",19  },20  "vrai-prix": {21    label: "Vrai-Prix",22    redirectPrefixes: ["https://www.vrai-prix.com/"],23    secretEnv: "KA_SSO_SECRET_VRAI_PRIX",24  },25  valoplex: {26    label: "ValoPlex",27    redirectPrefixes: ["https://www.valoplex.com/"],28    secretEnv: "KA_SSO_SECRET_VALOPLEX",29  },30  "auto-ka": {31    label: "Auto·Ka",32    redirectPrefixes: ["https://www.auto-ka.com/"],33    secretEnv: "KA_SSO_SECRET_AUTO_KA",34  },35  "fabri-ka": {36    label: "Fabri·Ka",37    redirectPrefixes: ["https://www.fabri-ka.com/"],38    secretEnv: "KA_SSO_SECRET_FABRI_KA",39  },40  "food-ka": {41    label: "Food·Ka",42    redirectPrefixes: ["https://www.food-ka.com/"],43    secretEnv: "KA_SSO_SECRET_FOOD_KA",44  },45  "ora-ka": {46    label: "Ora·Ka",47    redirectPrefixes: ["https://www.ora-ka.com/"],48    secretEnv: "KA_SSO_SECRET_ORA_KA",49  },50};5152/**53 * Vérifie une requête serveur-à-serveur d'une plateforme :54 * sig = HMAC-SHA256(secret_client, `${client_id}.${ka_id}.${ts}`), ts ±5 min.55 * Retourne le client si valide, sinon null.56 */57export function verifyClientSig(58  clientId: string,59  kaId: string,60  ts: string,61  sig: string,62): { label: string } | null {63  const client = SSO_CLIENTS[clientId];64  const secret = client ? process.env[client.secretEnv] : undefined;65  if (!client || !secret) return null;66  if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300)67    return null;68  const expected = crypto69    .createHmac("sha256", secret)70    .update(`${clientId}.${kaId}.${ts}`)71    .digest("hex");72  const a = Buffer.from(sig, "hex");73  const b = Buffer.from(expected, "hex");74  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) return null;75  return client;76}77