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 85.5% HTML 8.9% CSS 5.5%
2.4 KB · 64 lines typescript
Raw Blame History
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