PHP 8.2+ (Application web confidentielle)
Génération du défi PKCE en session, redirection vers login.orneos.eu, puis échange du code en cURL.
login.php & callback.php
Copier
<?php
session_start();
$clientId = 'orn_VOTRE_CLIENT_ID';
$clientSecret = 'orns_VOTRE_CLIENT_SECRET';
$redirectUri = 'https://example.com/callback.php';
if (!isset($_GET['code'])) {
$verifier = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');
$challenge = rtrim(strtr(base64_encode(hash('sha256', $verifier, true)), '+/', '-_'), '=');
$state = bin2hex(random_bytes(16));
$_SESSION['oauth'] = ['verifier' => $verifier, 'state' => $state];
header('Location: https://login.orneos.eu/oauth/authorize?' . http_build_query([
'client_id' => $clientId,
'redirect_uri' => $redirectUri,
'response_type' => 'code',
'scope' => 'openid profile email',
'state' => $state,
'code_challenge' => $challenge,
'code_challenge_method' => 'S256',
]));
exit;
}
if (!hash_equals($_SESSION['oauth']['state'] ?? '', $_GET['state'] ?? '')) {
http_response_code(400);
exit('Invalid state');
}
$ch = curl_init('https://orneos.eu/oauth/token');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERPWD => $clientId . ':' . $clientSecret,
CURLOPT_POSTFIELDS => http_build_query([
'grant_type' => 'authorization_code',
'code' => $_GET['code'],
'redirect_uri' => $redirectUri,
'code_verifier' => $_SESSION['oauth']['verifier'],
]),
]);
$tokens = json_decode(curl_exec($ch), true);
Node.js 20+ (Express / Fetch)
Utilisation du module natif node:crypto pour calculer le défi PKCE S256 et de fetch pour échanger le code.
orneos-auth.mjs
Copier
import crypto from 'node:crypto';
export function createPkcePair() {
const verifier = crypto.randomBytes(32).toString('base64url');
const challenge = crypto.createHash('sha256').update(verifier).digest('base64url');
return { verifier, challenge, state: crypto.randomBytes(16).toString('hex') };
}
export async function exchangeCode({ clientId, clientSecret, code, redirectUri, verifier }) {
const res = await fetch('https://orneos.eu/oauth/token', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + Buffer.from(`${clientId}:${clientSecret}`).toString('base64'),
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
redirect_uri: redirectUri,
code_verifier: verifier,
}),
});
return res.json();
}
Python 3.11+ (FastAPI / Requests)
Calcul du défi PKCE avec hashlib et secrets, puis récupération du profil sur /oauth/userinfo.
orneos_oauth.py
Copier
import base64, hashlib, secrets, requests
def generate_pkce():
verifier = secrets.token_urlsafe(32)
digest = hashlib.sha256(verifier.encode('ascii')).digest()
challenge = base64.urlsafe_b64encode(digest).rstrip(b'=').decode('ascii')
return verifier, challenge
def exchange_code(client_id: str, client_secret: str, code: str, redirect_uri: str, verifier: str) -> dict:
r = requests.post(
'https://orneos.eu/oauth/token',
auth=(client_id, client_secret),
data={
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': redirect_uri,
'code_verifier': verifier,
},
timeout=10,
)
r.raise_for_status()
return r.json()
SPA / Client public (Navigateur, sans secret)
Choisissez le type Client public (SPA / mobile) dans la console. Aucun client_secret n’est utilisé : l’échange se fait directement avec client_id et code_verifier.
pkce-spa.js
Copier
async function startOrneosLogin(clientId, redirectUri) {
const bytes = crypto.getRandomValues(new Uint8Array(32));
const b64 = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
const verifier = b64(bytes);
const challenge = b64(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier)));
const state = b64(crypto.getRandomValues(new Uint8Array(16)));
sessionStorage.setItem('orneos_pkce_verifier', verifier);
sessionStorage.setItem('orneos_oauth_state', state);
const params = new URLSearchParams({
client_id: clientId,
redirect_uri: redirectUri,
response_type: 'code',
scope: 'openid profile email',
state,
code_challenge: challenge,
code_challenge_method: 'S256',
});
window.location.assign('https://login.orneos.eu/oauth/authorize?' + params);
}
Aucune section ne correspond à votre recherche.