# Rayor Connect — « Se connecter avec Rayor » (SSO) Rayor Connect est le fournisseur d'identité (Single Sign-On) de l'écosystème Rayor. Il permet à un site tiers d'authentifier un utilisateur et de récupérer son profil. > Flux SIMPLIFIÉ inspiré d'OAuth2 — ce n'est PAS de l'OpenID Connect standard : > pas de `id_token`, pas d'endpoint `/token`, pas de `client_secret`. > Le `code` s'échange en simple GET contre le profil JSON. ## Identifiants - Aucun `client_secret`. - Le `client_id` est le **domaine** de votre site (ex. `mon-site.fr`). - Le `redirect_uri` DOIT être sur ce domaine (même hôte ou sous-domaine), sinon la requête est refusée. ## Flux en 3 étapes 1. Rediriger l'utilisateur vers l'écran de consentement : `GET https://connect.rayor.fr/authorize.php` Paramètres : - `client_id` (obligatoire) — votre domaine - `redirect_uri` (obligatoire) — URL de retour, même domaine que `client_id` - `scope` (obligatoire) — scopes séparés par des espaces (voir « Scopes ») - `response_type` (obligatoire) — `code` - `state` (recommandé) — valeur anti-CSRF à vérifier au retour 2. Rayor renvoie l'utilisateur sur `redirect_uri?code=CODE&state=STATE`. 3. Côté serveur, échanger le code (USAGE UNIQUE, expire ~5 min) : `GET https://connect.rayor.fr/api.php?code=CODE` → renvoie le profil JSON, **uniquement** les champs des scopes accordés. ## Scopes (minimisation RGPD : on ne renvoie QUE ce qui est demandé ET accordé) | scope | champs JSON renvoyés | |---------------|------------------------------------------------------| | openid | rayor_id (identifiant public stable) — le minimum | | profile | given_name, family_name, nickname, picture | | email | email, email_verified (bool) | | phone | phone_number, phone_number_verified (bool) | | address | address | | age | birthdate, age (int), age_verified (bool) | | subscription | rayor_plus (bool), rayor_plan ("plus" | "free") | | preferences | liquid_glass (bool) — préférence d'UI à refléter | Un scope refusé par l'utilisateur n'est PAS renvoyé : votre application doit gérer l'absence des champs. `picture` = avatar personnalisé sinon Gravatar. `email_verified` est toujours true. `age_verified` ne passe à true qu'après vérification d'identité. ## Exemples de réponse (même utilisateur, selon les scopes) `scope=openid` : ```json { "rayor_id": "RAY-42-XY" } ``` `scope=openid profile email` : ```json { "rayor_id": "RAY-42-XY", "given_name": "Camille", "family_name": "Martin", "nickname": "Camille", "picture": "https://connect.rayor.fr/avatar.php?u=…", "email": "camille@exemple.fr", "email_verified": true } ``` `scope=openid email subscription preferences` : ```json { "rayor_id": "RAY-42-XY", "email": "camille@exemple.fr", "email_verified": true, "rayor_plus": true, "rayor_plan": "plus", "liquid_glass": true } ``` ## Exemple PHP ```php // 1) Démarrage : rediriger vers Rayor header("Location: https://connect.rayor.fr/authorize.php?" . http_build_query([ "client_id" => "mon-site.fr", "redirect_uri" => "https://mon-site.fr/callback.php", "scope" => "openid profile email", "response_type" => "code", "state" => $_SESSION["rc_state"] = bin2hex(random_bytes(16)), ])); // 2) Sur callback.php : vérifier state puis échanger le code if (($_GET["state"] ?? "") !== ($_SESSION["rc_state"] ?? "x")) { http_response_code(400); exit("state invalide"); } $user = json_decode(file_get_contents( "https://connect.rayor.fr/api.php?code=" . urlencode($_GET["code"]) ), true); // $user["rayor_id"], $user["email"], $user["given_name"] ... ``` ## Exemple Node.js (Express) ```js import crypto from "node:crypto"; // 1) Démarrage app.get("/login", (req, res) => { req.session.rcState = crypto.randomBytes(16).toString("hex"); const q = new URLSearchParams({ client_id: "mon-site.fr", redirect_uri: "https://mon-site.fr/callback", scope: "openid profile email", response_type: "code", state: req.session.rcState, }); res.redirect("https://connect.rayor.fr/authorize.php?" + q); }); // 2) Callback : vérifier state puis échanger le code app.get("/callback", async (req, res) => { if (req.query.state !== req.session.rcState) return res.status(400).send("state invalide"); const r = await fetch("https://connect.rayor.fr/api.php?code=" + encodeURIComponent(req.query.code)); const user = await r.json(); // user.rayor_id, user.email, user.given_name ... }); ``` ## Exemple Python (Flask) ```python import secrets, requests from urllib.parse import urlencode from flask import redirect, request, session @app.get("/login") def login(): session["rc_state"] = secrets.token_hex(16) q = urlencode({ "client_id": "mon-site.fr", "redirect_uri": "https://mon-site.fr/callback", "scope": "openid profile email", "response_type": "code", "state": session["rc_state"], }) return redirect("https://connect.rayor.fr/authorize.php?" + q) @app.get("/callback") def callback(): if request.args.get("state") != session.get("rc_state"): return "state invalide", 400 user = requests.get("https://connect.rayor.fr/api.php", params={"code": request.args["code"]}, timeout=10).json() # user["rayor_id"], user["email"], user["given_name"] ... return user ``` ## Bouton HTML « Continuer avec Rayor » ```html Continuer avec Rayor ``` ## Sécurité - Générez et vérifiez `state` (anti-CSRF). - Le `code` est à usage unique et expire en ~5 min ; échangez-le **côté serveur**. - Ne demandez que les scopes nécessaires (RGPD) et gérez l'absence d'un scope refusé. ## Ressources - Manifeste machine : https://connect.rayor.fr/manifest - Documentation humaine : https://connect.rayor.fr/developers - Kit (plugin WordPress, SDK PHP, bouton) : https://connect.rayor.fr/rayor-connect-integration.zip - Contact : contact@rayor.fr