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 85.5%
HTML 8.9%
CSS 5.5%
1// Auteur : Simon-Pierre Boucher — contact@spboucher.ai2// Profil serveur-à-serveur pour les plateformes du groupe : le hub est LA3// source de vérité — Lou·Ka & cie affichent CE profil sur leurs pages4// utilisateur. Authentification par HMAC du secret SSO partagé du client.5// GET /api/sso/profile?client_id=lou-ka&ka_id=ka-…&ts=<unix>&sig=<hex>6// sig = HMAC-SHA256(secret_client, `${client_id}.${ka_id}.${ts}`), ts ±5 min7import { NextRequest, NextResponse } from "next/server";8import crypto from "crypto";9import { db, parseSocials, ageFromBirthDate, type UserRow } from "@/lib/db";10import { SSO_CLIENTS } from "@/lib/sso";11import { roleLabel } from "@/lib/roles";1213export async function GET(req: NextRequest) {14 const q = req.nextUrl.searchParams;15 const clientId = q.get("client_id") ?? "";16 const kaId = q.get("ka_id") ?? "";17 const ts = q.get("ts") ?? "";18 const sig = q.get("sig") ?? "";1920 const client = SSO_CLIENTS[clientId];21 const secret = client ? process.env[client.secretEnv] : undefined;22 if (!client || !secret)23 return NextResponse.json({ error: "client inconnu" }, { status: 401 });24 if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300)25 return NextResponse.json({ error: "horodatage expiré" }, { status: 401 });26 const expected = crypto27 .createHmac("sha256", secret)28 .update(`${clientId}.${kaId}.${ts}`)29 .digest("hex");30 const a = Buffer.from(sig, "hex");31 const b = Buffer.from(expected, "hex");32 if (a.length !== b.length || !crypto.timingSafeEqual(a, b))33 return NextResponse.json({ error: "signature invalide" }, { status: 401 });3435 const row = db36 .prepare("SELECT * FROM users WHERE ka_id = ?")37 .get(kaId) as UserRow | undefined;38 if (!row)39 return NextResponse.json({ error: "membre introuvable" }, { status: 404 });4041 return NextResponse.json({42 ka_id: row.ka_id,43 name: row.name,44 email: row.email,45 picture: row.avatar_url,46 role: row.role,47 role_label: roleLabel(row.role),48 bio: row.bio ?? "",49 city: row.city ?? "",50 phone: row.phone ?? "",51 website: row.website ?? "",52 job_title: row.job_title ?? "",53 company: row.company ?? "",54 birth_date: row.birth_date ?? "",55 age: ageFromBirthDate(row.birth_date),56 socials: parseSocials(row.socials),57 public: !!row.public,58 public_url: row.public59 ? `https://www.groupe-ka.com/u/${row.ka_id}`60 : null,61 created_at: row.created_at,62 });63}64