Introduction à Codea Auth
Système d'authentification universel pour vos applications web
Qu'est-ce que Codea Auth ?
Codea Auth est un service d'authentification centralisé qui vous permet d'ajouter un système de connexion complet à vos applications web en quelques lignes de code.
Plutôt que de gérer vous-même les comptes utilisateurs, les mots de passe, la sécurité et toute la complexité associée, vous déléguez cette responsabilité à Codea Auth.
Pensez à Codea Auth comme le "Se connecter avec Google" ou "Se connecter avec Facebook", mais pour vos propres applications.
Fonctionnalités principales
- Authentification sécurisée - Gestion complète des comptes utilisateurs
- OAuth 2.0 - Intégration facile avec tokens temporaires de 10 jours
- Multi-plateforme - Frontend, Backend, Mobile
- Protection anti-spam - Rate limiting et hCaptcha intégré
- Gestion de profils - Photos, informations personnelles, confidentialité
- � Récupération de mot de passe - Envoi d'emails automatique
- Popup élégante - Interface utilisateur moderne et responsive
Comment ça marche ?
1Vous créez une application dans le dashboard Codea Auth
2Vous intégrez le SDK dans votre site (1 ligne de code)
3Vos utilisateurs se connectent via une popup sécurisée
4Vous recevez leurs informations (nom, email, photo, etc.)
Comparaison avec d'autres solutions
| Fonctionnalité | Codea Auth | Auth0 | Firebase | DIY |
|---|---|---|---|---|
| Gratuit | Oui | Limité | Limité | Oui |
| Facile à intégrer | Très facile | Moyen | Facile | Difficile |
| UI incluse | Oui | Non | Non | Non |
| OAuth sécurisé | Oui | Oui | Oui | À faire |
| Protection anti-spam | hCaptcha | Oui | Limité | À faire |
Passez à la section Démarrage rapide pour créer votre première application en 5 minutes !
Démarrage rapide
Intégrez Codea Auth en 5 minutes chrono
� Prérequis
- Un compte Codea Auth → S'inscrire gratuitement
- Un site web (HTML, React, Vue, ou n'importe quel framework)
Étape 1 : Créer une application
1Connectez-vous à votre compte
Allez sur Mes Applications
2Cliquez sur "Créer une application"
Remplissez les informations :
- Nom : Mon Super Site
- Description : (optionnel)
- URL de redirection : https://mon-site.com
3Récupérez vos credentials
Vous obtiendrez :
- App ID :
590551(6 chiffres) - Secret Key :
00a75ea7f31ee...(64 caractères)
Ne partagez JAMAIS votre Secret Key publiquement. Stockez-la dans un fichier .env ou dans les variables d'environnement de votre serveur.
Étape 2 : Intégrer le SDK
Option A : Frontend simple (HTML/JavaScript)
<!DOCTYPE html>
<html>
<head>
<title>Mon Site</title>
</head>
<body>
<button onclick="auth.login()">Se connecter</button>
<div id="user-info" style="display:none;">
<p>Bienvenue <span id="username"></span></p>
</div>
<!-- SDK Codea Auth -->
<script src="https://auth.codealuxz.fr/codea-auth-sdk.js"></script>
<script>
const auth = new CodeaAuth({
appId: '590551',
secretKey: 'VOTRE_SECRET_KEY',
onSuccess: function(result) {
console.log('Utilisateur connecté:', result.user);
// Stocker le token
localStorage.setItem('userToken', result.token);
// Afficher les infos
document.getElementById('username').textContent = result.user.username;
document.getElementById('user-info').style.display = 'block';
},
onError: function(error) {
console.error('Erreur:', error);
alert('Erreur de connexion: ' + error.message);
}
});
</script>
</body>
</html>
Option B : Backend sécurisé (Recommandé)
Pour ne pas exposer votre Secret Key, utilisez le système OAuth :
// Frontend : Récupérer le token utilisateur
const auth = new CodeaAuth({
appId: '590551', // Public - OK d'exposer
secretKey: 'SECRET', // NE PAS EXPOSER EN PROD
onSuccess: async function(result) {
// Envoyer le token à votre backend pour vérification
const response = await fetch('/api/verify-user', {
method: 'POST',
headers: { 'Authorization': `Bearer ${result.token}` }
});
const data = await response.json();
console.log('Utilisateur vérifié:', data.user);
}
});
// Backend : Vérifier le token utilisateur
app.post('/api/verify-user', async (req, res) => {
const userToken = req.headers.authorization?.replace('Bearer ', '');
// 1. Obtenir un access token (caché 10 jours)
const accessTokenResponse = await fetch('https://auth.codealuxz.fr/api/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
appId: process.env.CODEA_APP_ID,
secretKey: process.env.CODEA_SECRET_KEY
})
});
const { access_token } = await accessTokenResponse.json();
// 2. Vérifier l'utilisateur
const verifyResponse = await fetch('https://auth.codealuxz.fr/api/oauth/verify-user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
access_token: access_token,
user_token: userToken
})
});
const data = await verifyResponse.json();
if (data.valid) {
res.json({ user: data.user });
} else {
res.status(401).json({ error: data.error });
}
});
C'est tout !
Vous avez maintenant un système d'authentification complet qui gère :
- Inscription des utilisateurs
- Connexion / Déconnexion
- Gestion des profils
- Récupération de mot de passe
- Sessions multiples
- Protection anti-spam
Consultez la section Comprendre OAuth pour sécuriser votre intégration avec des tokens temporaires.
Comprendre le système OAuth
Sécurisez vos credentials avec des tokens temporaires
Pourquoi OAuth ?
Imaginons que vous intégriez Codea Auth dans votre site. Vous avez deux credentials importants :
- App ID :
590551(public, OK de partager) - Secret Key :
00a75ea7...(privé, NE JAMAIS partager)
Si vous utilisez directement votre Secret Key dans le frontend (navigateur), n'importe qui peut l'inspecter dans le code source et usurper votre application !
Solution : Tokens temporaires
Au lieu d'utiliser directement votre Secret Key, vous l'échangez contre un Access Token temporaire qui :
- Expire après 10 jours
- Peut être révoqué à tout moment
- Ne compromet pas votre Secret Key
- Est stocké seulement côté backend
Flux OAuth
┌──────────────┐
│ Frontend │
│ (Navigateur)│
└──────┬───────┘
│
│ 1. Utilisateur se connecte
│ via popup Codea Auth
│
▼
┌──────────────┐
│ Popup Codea │ ─────┐
│ Auth │ │ 2. Retourne token utilisateur
└──────────────┘ │
│
┌──────────────┘
│
▼
┌──────────────┐
│ Frontend │ ────────────────┐
└──────────────┘ │
│ 3. Envoie token au backend
│
┌─────────────────────────┘
│
▼
┌──────────────┐
│ Backend │
│ (Serveur) │
└──────┬───────┘
│
│ 4. Échange Secret Key
│ contre Access Token
│
▼
┌──────────────┐
│ Codea Auth │
│ API │
└──────┬───────┘
│
│ 5. Retourne Access Token
│ (valide 10 jours)
│
▼
┌──────────────┐
│ Backend │
│ (cache) │
└──────┬───────┘
│
│ 6. Vérifie token utilisateur
│ avec Access Token
│
▼
┌──────────────┐
│ Codea Auth │
│ API │
└──────┬───────┘
│
│ 7. Retourne infos utilisateur
│
▼
┌──────────────┐
│ Backend │ ──────┐
└──────────────┘ │ 8. Envoie au frontend
│
┌───────────────┘
│
▼
┌──────────────┐
│ Frontend │
│ (résultat) │
└──────────────┘
Les 3 types de tokens
| Type | Description | Durée | Où stocker |
|---|---|---|---|
| Secret Key | Credential permanent de votre app | Illimité | Backend seulement (.env) |
| Access Token | Token temporaire pour l'app | 10 jours | Backend (cache/DB) |
| User Token | Token de session utilisateur | Jusqu'à déco | Frontend (localStorage) |
Implémentation
1. Obtenir un Access Token (Backend)
const response = await fetch('https://auth.codealuxz.fr/api/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
appId: process.env.CODEA_APP_ID,
secretKey: process.env.CODEA_SECRET_KEY
})
});
const data = await response.json();
// {
// "access_token": "a1b2c3d4...",
// "token_type": "Bearer",
// "expires_in": 864000, // 10 jours en secondes
// "expires_at": "2025-10-16T..."
// }
2. Vérifier un utilisateur avec l'Access Token
const response = await fetch('https://auth.codealuxz.fr/api/oauth/verify-user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
access_token: 'VOTRE_ACCESS_TOKEN',
user_token: 'TOKEN_UTILISATEUR'
})
});
const data = await response.json();
// {
// "valid": true,
// "user": {
// "username": "johndoe",
// "firstName": "John",
// "lastName": "Doe",
// "email": "[email protected]",
// ...
// },
// "app": {
// "appId": "590551",
// "name": "Mon Application"
// }
// }
3. Utiliser le SDK (automatique)
const auth = new CodeaAuth({
appId: process.env.CODEA_APP_ID,
secretKey: process.env.CODEA_SECRET_KEY,
mode: 'backend'
});
// Le SDK gère automatiquement l'Access Token
const result = await auth.verifyUserToken(userToken);
if (result.valid) {
console.log('Utilisateur:', result.user);
} else {
console.error('Token invalide:', result.error);
}
Le SDK cache automatiquement l'Access Token pendant 10 jours. Vous n'avez pas besoin de le gérer manuellement !
Gestion de l'expiration
Les Access Tokens expirent après 10 jours. Voici comment gérer le renouvellement :
class TokenManager {
constructor(appId, secretKey) {
this.appId = appId;
this.secretKey = secretKey;
this.token = null;
this.expiry = null;
}
async getToken() {
// Si token valide, le retourner
if (this.token && this.expiry && new Date() < new Date(this.expiry)) {
return this.token;
}
// Sinon, en générer un nouveau
const response = await fetch('https://auth.codealuxz.fr/api/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
appId: this.appId,
secretKey: this.secretKey
})
});
const data = await response.json();
this.token = data.access_token;
this.expiry = data.expires_at;
return this.token;
}
}
// Utilisation
const tokenManager = new TokenManager(
process.env.CODEA_APP_ID,
process.env.CODEA_SECRET_KEY
);
// Automatiquement renouvelé si expiré
const accessToken = await tokenManager.getToken();
Révoquer un Access Token
Si vous pensez qu'un Access Token a été compromis, vous pouvez le révoquer :
await fetch('https://auth.codealuxz.fr/api/oauth/revoke', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
access_token: 'TOKEN_A_REVOQUER'
})
});
// Le token est immédiatement invalidé
Maintenant que vous comprenez OAuth, apprenez comment intégrer Codea Auth côté backend de manière sécurisée.
Intégration Frontend
Intégrer la popup de connexion dans votre site web
� Vue d'ensemble
L'intégration frontend utilise le SDK JavaScript de Codea Auth pour afficher une popup élégante qui gère toute l'authentification. C'est la méthode la plus simple et la plus rapide.
- Sites statiques (HTML, CSS, JS)
- Applications Single Page (React, Vue, Angular)
- Prototypage rapide
- Projets sans backend
Installation
Méthode 1 : CDN (Recommandé)
<script src="https://auth.codealuxz.fr/codea-auth-sdk.js"></script>
Méthode 2 : NPM (Pour React, Vue, etc.)
npm install @codealuxz/auth-sdk
import CodeaAuth from '@codealuxz/auth-sdk';
Configuration
const auth = new CodeaAuth({
appId: '590551', // Votre App ID (requis)
secretKey: 'your_secret_key', // Votre Secret Key (requis)
redirectUri: 'https://mon-site.com', // URL de redirection (optionnel)
mode: 'frontend', // Mode d'utilisation (optionnel)
// Callbacks
onSuccess: function(result) {
// Appelé quand l'utilisateur se connecte avec succès
console.log('Token:', result.token);
console.log('Utilisateur:', result.user);
},
onError: function(error) {
// Appelé en cas d'erreur
console.error('Erreur:', error.message);
}
});
Objets retournés
onSuccess - Objet result
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "507f1f77bcf86cd799439011",
"username": "johndoe",
"firstName": "John",
"lastName": "Doe",
"email": "[email protected]",
"phoneNumber": "+33612345678",
"dateOfBirth": "1990-01-01",
"profilePicture": "data:image/jpeg;base64,...",
"createdAt": "2025-01-01T00:00:00.000Z",
"privacy": {
"hideFirstName": false,
"hideLastName": false,
"hideDateOfBirth": true,
"hideEmail": false
}
}
}
Méthodes du SDK
| Méthode | Description | Retour |
|---|---|---|
auth.login() |
Ouvre la popup de connexion/inscription | void |
auth.verifyApp() |
Vérifie les credentials de l'app | Promise<Object> |
auth.getAuthorizedUsers() |
Récupère les utilisateurs autorisés | Promise<Array> |
auth.getAccessToken() |
Obtient un access token (mode backend) | Promise<string> |
auth.verifyUserToken(token) |
Vérifie un token utilisateur | Promise<Object> |
Exemples d'utilisation
Exemple 1 : HTML/Vanilla JS
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Mon Site</title>
<style>
.hidden { display: none; }
.user-card {
background: #f5f5f5;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}
</style>
</head>
<body>
<div id="login-section">
<h1>Bienvenue</h1>
<button onclick="auth.login()" class="btn-login">
Se connecter avec Codea Auth
</button>
</div>
<div id="user-section" class="hidden">
<div class="user-card">
<img id="user-avatar" src="" alt="Avatar" width="80" height="80">
<h2>Bienvenue <span id="user-name"></span></h2>
<p>Email : <span id="user-email"></span></p>
<p>Membre depuis : <span id="user-since"></span></p>
<button onclick="logout()">Déconnexion</button>
</div>
</div>
<script src="https://auth.codealuxz.fr/codea-auth-sdk.js"></script>
<script>
const auth = new CodeaAuth({
appId: '590551',
secretKey: 'votre_secret_key',
onSuccess: function(result) {
// Stocker le token
localStorage.setItem('userToken', result.token);
localStorage.setItem('userData', JSON.stringify(result.user));
// Afficher l'utilisateur
displayUser(result.user);
},
onError: function(error) {
alert('Erreur de connexion : ' + error.message);
}
});
// Afficher les informations utilisateur
function displayUser(user) {
document.getElementById('login-section').classList.add('hidden');
document.getElementById('user-section').classList.remove('hidden');
document.getElementById('user-name').textContent = user.username;
document.getElementById('user-email').textContent = user.email || 'Non renseigné';
document.getElementById('user-avatar').src = user.profilePicture || 'default-avatar.png';
const since = new Date(user.createdAt).toLocaleDateString('fr-FR');
document.getElementById('user-since').textContent = since;
}
// Déconnexion
function logout() {
localStorage.removeItem('userToken');
localStorage.removeItem('userData');
document.getElementById('login-section').classList.remove('hidden');
document.getElementById('user-section').classList.add('hidden');
}
// Vérifier si déjà connecté au chargement
window.addEventListener('DOMContentLoaded', function() {
const userData = localStorage.getItem('userData');
if (userData) {
try {
const user = JSON.parse(userData);
displayUser(user);
} catch (e) {
console.error('Erreur parsing user data:', e);
}
}
});
</script>
</body>
</html>
Exemple 2 : React
import { useState, useEffect } from 'react';
function App() {
const [user, setUser] = useState(null);
const [auth, setAuth] = useState(null);
useEffect(() => {
// Initialiser le SDK
const codeaAuth = new window.CodeaAuth({
appId: '590551',
secretKey: process.env.REACT_APP_CODEA_SECRET,
onSuccess: (result) => {
setUser(result.user);
localStorage.setItem('userToken', result.token);
},
onError: (error) => {
alert('Erreur : ' + error.message);
}
});
setAuth(codeaAuth);
// Vérifier si déjà connecté
const savedUser = localStorage.getItem('userData');
if (savedUser) {
setUser(JSON.parse(savedUser));
}
}, []);
const handleLogin = () => {
auth?.login();
};
const handleLogout = () => {
setUser(null);
localStorage.removeItem('userToken');
localStorage.removeItem('userData');
};
if (user) {
return (
<div className="user-dashboard">
<img src={user.profilePicture} alt="Avatar" />
<h1>Bienvenue {user.username}</h1>
<p>Email : {user.email}</p>
<button onClick={handleLogout}>Déconnexion</button>
</div>
);
}
return (
<div className="login-page">
<h1>Mon Application</h1>
<button onClick={handleLogin}>
Se connecter avec Codea Auth
</button>
</div>
);
}
export default App;
Exemple 3 : Vue.js
<template>
<div id="app">
<div v-if="!user" class="login-section">
<h1>Bienvenue</h1>
<button @click="login">Se connecter avec Codea Auth</button>
</div>
<div v-else class="user-section">
<img :src="user.profilePicture" alt="Avatar" width="80" />
<h2>Bienvenue {{ user.username }}</h2>
<p>Email : {{ user.email }}</p>
<button @click="logout">Déconnexion</button>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
user: null,
auth: null
}
},
mounted() {
this.auth = new window.CodeaAuth({
appId: '590551',
secretKey: process.env.VUE_APP_CODEA_SECRET,
onSuccess: (result) => {
this.user = result.user;
localStorage.setItem('userToken', result.token);
localStorage.setItem('userData', JSON.stringify(result.user));
},
onError: (error) => {
alert('Erreur : ' + error.message);
}
});
// Vérifier si déjà connecté
const savedUser = localStorage.getItem('userData');
if (savedUser) {
this.user = JSON.parse(savedUser);
}
},
methods: {
login() {
this.auth.login();
},
logout() {
this.user = null;
localStorage.removeItem('userToken');
localStorage.removeItem('userData');
}
}
}
</script>
En mode frontend, votre Secret Key est exposée dans le code JavaScript. Pour une sécurité maximale, utilisez plutôt l'intégration backend avec OAuth.
Gestion des sessions
Le token utilisateur doit être stocké et vérifié à chaque chargement de page :
// Au chargement de la page
window.addEventListener('DOMContentLoaded', async function() {
const token = localStorage.getItem('userToken');
if (token) {
// Vérifier si le token est toujours valide
const response = await fetch('https://auth.codealuxz.fr/api/auth/verify-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token })
});
const data = await response.json();
if (data.valid) {
// Token valide, afficher l'interface utilisateur
displayUser(data.user);
} else {
// Token invalide, nettoyer
localStorage.removeItem('userToken');
localStorage.removeItem('userData');
}
}
});
Vous savez maintenant intégrer Codea Auth côté frontend ! Pour une architecture plus sécurisée, consultez l'intégration backend.
Intégration Backend
Vérifier et gérer les utilisateurs côté serveur
� Vue d'ensemble
L'intégration backend vous permet de sécuriser vos credentials et de vérifier les utilisateurs côté serveur. Cette approche est fortement recommandée pour la production.
- Secret Key jamais exposée publiquement
- Tokens OAuth temporaires (10 jours)
- Validation serveur des utilisateurs
- Contrôle total sur l'authentification
Architecture
Frontend (Browser) Backend (Server) Codea Auth API
│ │ │
│ 1. User clicks login │ │
│─────────────────────────> │ │
│ │ │
│ 2. Open Codea popup │ │
│<───────────────────────── │ │
│ │ │
│ 3. User authenticates │ │
│────────────────────────────────────────────────────> │
│ │ │
│ 4. Return user token │ │
│<──────────────────────────────────────────────────── │
│ │ │
│ 5. Send token to backend │ │
│─────────────────────────> │ │
│ │ 6. Get access token │
│ │─────────────────────────>│
│ │ │
│ │ 7. Return access token │
│ │<─────────────────────────│
│ │ │
│ │ 8. Verify user token │
│ │─────────────────────────>│
│ │ │
│ │ 9. Return user data │
│ │<─────────────────────────│
│ │ │
│ 10. Return user to front │ │
│<───────────────────────── │ │
Configuration
Étape 1 : Variables d'environnement
CODEA_APP_ID=590551
CODEA_SECRET_KEY=00a75ea7f31ee220ea6414d69de7b85b8113b65276e62f74f0403930f333bdb8
CODEA_AUTH_URL=https://auth.codealuxz.fr
Ne JAMAIS committer le fichier .env dans Git ! Ajoutez-le au .gitignore
Étape 2 : Installation des dépendances
Node.js
npm install dotenv node-fetch express
Python
pip install python-dotenv requests flask
PHP
composer require vlucas/phpdotenv guzzlehttp/guzzle
Implémentation
Node.js / Express
require('dotenv').config();
const express = require('express');
const fetch = require('node-fetch');
const app = express();
app.use(express.json());
// Cache pour l'access token
let cachedAccessToken = null;
let tokenExpiry = null;
// Fonction pour obtenir un access token valide
async function getValidAccessToken() {
// Si le token est encore valide, le retourner
if (cachedAccessToken && tokenExpiry && new Date() < new Date(tokenExpiry)) {
return cachedAccessToken;
}
// Sinon, en générer un nouveau
const response = await fetch(`${process.env.CODEA_AUTH_URL}/api/oauth/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
appId: process.env.CODEA_APP_ID,
secretKey: process.env.CODEA_SECRET_KEY
})
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Erreur génération token');
}
cachedAccessToken = data.access_token;
tokenExpiry = data.expires_at;
return cachedAccessToken;
}
// Route pour vérifier un utilisateur
app.post('/api/verify-user', async (req, res) => {
try {
const userToken = req.headers.authorization?.replace('Bearer ', '');
if (!userToken) {
return res.status(401).json({ error: 'Token requis' });
}
// Obtenir un access token
const accessToken = await getValidAccessToken();
// Vérifier l'utilisateur
const response = await fetch(`${process.env.CODEA_AUTH_URL}/api/oauth/verify-user`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
access_token: accessToken,
user_token: userToken
})
});
const data = await response.json();
if (data.valid) {
res.json({
success: true,
user: data.user
});
} else {
res.status(401).json({
success: false,
error: data.error
});
}
} catch (error) {
console.error('Erreur vérification:', error);
res.status(500).json({ error: 'Erreur serveur' });
}
});
// Middleware d'authentification réutilisable
async function authenticateUser(req, res, next) {
try {
const userToken = req.headers.authorization?.replace('Bearer ', '');
if (!userToken) {
return res.status(401).json({ error: 'Non authentifié' });
}
const accessToken = await getValidAccessToken();
const response = await fetch(`${process.env.CODEA_AUTH_URL}/api/oauth/verify-user`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
access_token: accessToken,
user_token: userToken
})
});
const data = await response.json();
if (data.valid) {
req.user = data.user;
next();
} else {
res.status(401).json({ error: 'Token invalide' });
}
} catch (error) {
res.status(500).json({ error: 'Erreur serveur' });
}
}
// Route protégée exemple
app.get('/api/user/profile', authenticateUser, (req, res) => {
res.json({
message: 'Profil utilisateur',
user: req.user
});
});
app.listen(3000, () => {
console.log('Serveur démarré sur le port 3000');
});
Python / Flask
import os
from datetime import datetime
from flask import Flask, request, jsonify
from dotenv import load_dotenv
import requests
load_dotenv()
app = Flask(__name__)
# Cache pour l'access token
cached_access_token = None
token_expiry = None
def get_valid_access_token():
"""Obtient un access token valide (avec cache)"""
global cached_access_token, token_expiry
# Si le token est encore valide, le retourner
if cached_access_token and token_expiry:
if datetime.now() < datetime.fromisoformat(token_expiry.replace('Z', '+00:00')):
return cached_access_token
# Sinon, en générer un nouveau
response = requests.post(
f"{os.getenv('CODEA_AUTH_URL')}/api/oauth/token",
json={
'appId': os.getenv('CODEA_APP_ID'),
'secretKey': os.getenv('CODEA_SECRET_KEY')
}
)
data = response.json()
if response.status_code != 200:
raise Exception(data.get('error', 'Erreur génération token'))
cached_access_token = data['access_token']
token_expiry = data['expires_at']
return cached_access_token
@app.route('/api/verify-user', methods=['POST'])
def verify_user():
"""Vérifie un token utilisateur"""
try:
auth_header = request.headers.get('Authorization', '')
user_token = auth_header.replace('Bearer ', '')
if not user_token:
return jsonify({'error': 'Token requis'}), 401
# Obtenir un access token
access_token = get_valid_access_token()
# Vérifier l'utilisateur
response = requests.post(
f"{os.getenv('CODEA_AUTH_URL')}/api/oauth/verify-user",
json={
'access_token': access_token,
'user_token': user_token
}
)
data = response.json()
if data.get('valid'):
return jsonify({
'success': True,
'user': data['user']
})
else:
return jsonify({
'success': False,
'error': data.get('error')
}), 401
except Exception as e:
return jsonify({'error': str(e)}), 500
# Décorateur d'authentification
def require_auth(f):
"""Décorateur pour protéger les routes"""
from functools import wraps
@wraps(f)
def decorated_function(*args, **kwargs):
auth_header = request.headers.get('Authorization', '')
user_token = auth_header.replace('Bearer ', '')
if not user_token:
return jsonify({'error': 'Non authentifié'}), 401
try:
access_token = get_valid_access_token()
response = requests.post(
f"{os.getenv('CODEA_AUTH_URL')}/api/oauth/verify-user",
json={
'access_token': access_token,
'user_token': user_token
}
)
data = response.json()
if data.get('valid'):
request.user = data['user']
return f(*args, **kwargs)
else:
return jsonify({'error': 'Token invalide'}), 401
except Exception as e:
return jsonify({'error': str(e)}), 500
return decorated_function
# Route protégée exemple
@app.route('/api/user/profile')
@require_auth
def get_profile():
return jsonify({
'message': 'Profil utilisateur',
'user': request.user
})
if __name__ == '__main__':
app.run(debug=True, port=3000)
PHP
<?php
require 'vendor/autoload.php';
use Dotenv\Dotenv;
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
// Cache pour l'access token (utiliser Redis en production)
$cacheFile = __DIR__ . '/cache/access_token.json';
function getValidAccessToken() {
global $cacheFile;
// Vérifier le cache
if (file_exists($cacheFile)) {
$cache = json_decode(file_get_contents($cacheFile), true);
if (strtotime($cache['expires_at']) > time()) {
return $cache['access_token'];
}
}
// Générer un nouveau token
$ch = curl_init($_ENV['CODEA_AUTH_URL'] . '/api/oauth/token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'appId' => $_ENV['CODEA_APP_ID'],
'secretKey' => $_ENV['CODEA_SECRET_KEY']
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($response, true);
if ($httpCode !== 200) {
throw new Exception($data['error'] ?? 'Erreur génération token');
}
// Sauvegarder en cache
if (!is_dir(dirname($cacheFile))) {
mkdir(dirname($cacheFile), 0755, true);
}
file_put_contents($cacheFile, json_encode($data));
return $data['access_token'];
}
function verifyUserToken($userToken) {
$accessToken = getValidAccessToken();
$ch = curl_init($_ENV['CODEA_AUTH_URL'] . '/api/oauth/verify-user');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'access_token' => $accessToken,
'user_token' => $userToken
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// Router simple
$method = $_SERVER['REQUEST_METHOD'];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
header('Content-Type: application/json');
if ($method === 'POST' && $path === '/api/verify-user') {
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
$userToken = str_replace('Bearer ', '', $authHeader);
if (empty($userToken)) {
http_response_code(401);
echo json_encode(['error' => 'Token requis']);
exit;
}
try {
$result = verifyUserToken($userToken);
if ($result['valid']) {
echo json_encode([
'success' => true,
'user' => $result['user']
]);
} else {
http_response_code(401);
echo json_encode([
'success' => false,
'error' => $result['error']
]);
}
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
exit;
}
// Route protégée exemple
if ($method === 'GET' && $path === '/api/user/profile') {
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
$userToken = str_replace('Bearer ', '', $authHeader);
if (empty($userToken)) {
http_response_code(401);
echo json_encode(['error' => 'Non authentifié']);
exit;
}
try {
$result = verifyUserToken($userToken);
if ($result['valid']) {
echo json_encode([
'message' => 'Profil utilisateur',
'user' => $result['user']
]);
} else {
http_response_code(401);
echo json_encode(['error' => 'Token invalide']);
}
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
exit;
}
http_response_code(404);
echo json_encode(['error' => 'Route non trouvée']);
?>
Utilisation avec le frontend
const auth = new CodeaAuth({
appId: '590551', // Public - OK d'exposer
secretKey: 'FAKE_KEY', // Pas utilisé par le backend
onSuccess: async function(result) {
const userToken = result.token;
// Envoyer le token au backend pour vérification
const response = await fetch('/api/verify-user', {
method: 'POST',
headers: {
'Authorization': `Bearer ${userToken}`,
'Content-Type': 'application/json'
}
});
const data = await response.json();
if (data.success) {
console.log('Utilisateur vérifié:', data.user);
// Stocker le token pour les futures requêtes
localStorage.setItem('userToken', userToken);
} else {
alert('Erreur de vérification : ' + data.error);
}
}
});
// Utiliser le token pour les requêtes protégées
async function getUserProfile() {
const token = localStorage.getItem('userToken');
const response = await fetch('/api/user/profile', {
headers: {
'Authorization': `Bearer ${token}`
}
});
const data = await response.json();
console.log('Profil:', data.user);
}
Vous avez maintenant une architecture backend sécurisée. Consultez la section Full-Stack pour voir un exemple complet.
Intégration Backend
Vérifier les utilisateurs côté serveur
Section complétée
Voir ci-dessus pour le contenu complet
Intégration Full-Stack
Architecture complète frontend + backend
Architecture Full-Stack Recommandée
Voici l'architecture complète combinant frontend et backend pour une sécurité maximale.
� Structure du projet
my-app/
├── frontend/
│ └── index.html
├── backend/
│ ├── server.js
│ ├── .env
│ └── package.json
└── README.md
Frontend (index.html)
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Mon App Full-Stack</title>
<style>
.hidden { display: none; }
.container { max-width: 800px; margin: 50px auto; padding: 20px; }
.user-card { background: #f5f5f5; padding: 20px; border-radius: 8px; }
.btn { padding: 10px 20px; cursor: pointer; }
</style>
</head>
<body>
<div class="container">
<div id="login-section">
<h1>Bienvenue sur Mon App</h1>
<button class="btn" onclick="auth.login()">
Se connecter avec Codea Auth
</button>
</div>
<div id="user-section" class="hidden">
<div class="user-card">
<h2>Tableau de bord</h2>
<img id="avatar" width="80" height="80" />
<p><strong>Nom :</strong> <span id="name"></span></p>
<p><strong>Email :</strong> <span id="email"></span></p>
<button class="btn" onclick="logout()">Déconnexion</button>
</div>
</div>
</div>
<script src="https://auth.codealuxz.fr/codea-auth-sdk.js"></script>
<script>
const API_URL = 'http://localhost:3000';
const auth = new CodeaAuth({
appId: '590551',
secretKey: 'PUBLIC_FAKE_KEY', // Ne sera pas utilisé par le backend
onSuccess: async (result) => {
const userToken = result.token;
// Envoyer au backend pour vérification sécurisée
try {
const response = await fetch(`${API_URL}/api/verify`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${userToken}`,
'Content-Type': 'application/json'
}
});
const data = await response.json();
if (data.valid) {
// Stocker le token
localStorage.setItem('userToken', userToken);
localStorage.setItem('userData', JSON.stringify(data.user));
// Afficher l'utilisateur
displayUser(data.user);
} else {
alert('Erreur de vérification : ' + data.error);
}
} catch (error) {
console.error('Erreur:', error);
alert('Erreur de connexion au serveur');
}
},
onError: (error) => {
alert('Erreur : ' + error.message);
}
});
function displayUser(user) {
document.getElementById('login-section').classList.add('hidden');
document.getElementById('user-section').classList.remove('hidden');
document.getElementById('name').textContent = `${user.firstName} ${user.lastName}`;
document.getElementById('email').textContent = user.email || 'Non renseigné';
document.getElementById('avatar').src = user.profilePicture || 'default.png';
}
function logout() {
localStorage.clear();
document.getElementById('login-section').classList.remove('hidden');
document.getElementById('user-section').classList.add('hidden');
}
// Vérifier au chargement si déjà connecté
window.addEventListener('DOMContentLoaded', async () => {
const token = localStorage.getItem('userToken');
if (token) {
try {
const res = await fetch(`${API_URL}/api/verify`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` }
});
const data = await res.json();
if (data.valid) {
displayUser(data.user);
} else {
localStorage.clear();
}
} catch (e) {
localStorage.clear();
}
}
});
</script>
</body>
</html>
Backend (server.js)
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const fetch = require('node-fetch');
const app = express();
app.use(cors());
app.use(express.json());
// Cache pour l'access token
let cachedAccessToken = null;
let tokenExpiry = null;
// Fonction pour obtenir un access token valide
async function getValidAccessToken() {
if (cachedAccessToken && tokenExpiry && new Date() < new Date(tokenExpiry)) {
console.log(' Utilisation du token en cache');
return cachedAccessToken;
}
console.log(' Génération d\'un nouveau access token...');
const response = await fetch('https://auth.codealuxz.fr/api/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
appId: process.env.CODEA_APP_ID,
secretKey: process.env.CODEA_SECRET_KEY
})
});
if (!response.ok) {
throw new Error('Erreur génération access token');
}
const data = await response.json();
cachedAccessToken = data.access_token;
tokenExpiry = data.expires_at;
console.log(` Access token généré (expire: ${tokenExpiry})`);
return cachedAccessToken;
}
// Route de vérification
app.post('/api/verify', async (req, res) => {
try {
const userToken = req.headers.authorization?.replace('Bearer ', '');
if (!userToken) {
return res.status(401).json({
valid: false,
error: 'Token manquant'
});
}
// Obtenir access token
const accessToken = await getValidAccessToken();
// Vérifier le token utilisateur
const verifyResponse = await fetch('https://auth.codealuxz.fr/api/oauth/verify-user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
access_token: accessToken,
user_token: userToken
})
});
const data = await verifyResponse.json();
if (data.valid) {
console.log(` Utilisateur vérifié: ${data.user.username}`);
res.json({
valid: true,
user: data.user
});
} else {
console.log(' Token invalide');
res.status(401).json({
valid: false,
error: data.error
});
}
} catch (error) {
console.error(' Erreur:', error);
res.status(500).json({
valid: false,
error: 'Erreur serveur'
});
}
});
// Middleware d'authentification réutilisable
async function requireAuth(req, res, next) {
try {
const userToken = req.headers.authorization?.replace('Bearer ', '');
if (!userToken) {
return res.status(401).json({ error: 'Non authentifié' });
}
const accessToken = await getValidAccessToken();
const response = await fetch('https://auth.codealuxz.fr/api/oauth/verify-user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
access_token: accessToken,
user_token: userToken
})
});
const data = await response.json();
if (data.valid) {
req.user = data.user;
next();
} else {
res.status(401).json({ error: 'Token invalide' });
}
} catch (error) {
res.status(500).json({ error: 'Erreur serveur' });
}
}
// Exemple de route protégée
app.get('/api/user/profile', requireAuth, (req, res) => {
res.json({
message: 'Profil utilisateur',
user: req.user
});
});
// Exemple de route protégée avec données
app.get('/api/user/dashboard', requireAuth, (req, res) => {
res.json({
user: req.user,
stats: {
loginCount: 42,
lastLogin: new Date()
}
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(` Serveur démarré sur http://localhost:${PORT}`);
});
Fichier .env
CODEA_APP_ID=590551
CODEA_SECRET_KEY=00a75ea7f31ee220ea6414d69de7b85b8113b65276e62f74f0403930f333bdb8
PORT=3000
package.json
{
"name": "my-fullstack-app",
"version": "1.0.0",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"node-fetch": "^2.6.7"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}
Lancement
# Installer les dépendances
npm install
# Lancer le serveur
npm start
# Ouvrir frontend/index.html dans votre navigateur
Cette architecture garantit que votre Secret Key n'est jamais exposée au client et que toutes les vérifications sont faites côté serveur.
API OAuth & Tokens
Référence complète de l'API OAuth
API OAuth - Référence complète
L'API OAuth permet de sécuriser vos credentials avec des tokens temporaires de 10 jours.
POST /api/oauth/token
Obtenir un access token en échangeant vos credentials.
Request
{
"appId": "590551",
"secretKey": "00a75ea7f31ee220ea6414d69de7b85b8113b65276e62f74f0403930f333bdb8"
}
Response (200 OK)
{
"access_token": "a1b2c3d4e5f6789012345678901234567890123456789012345678901234",
"token_type": "Bearer",
"expires_in": 864000,
"expires_at": "2025-10-16T20:00:00.000Z"
}
Erreurs possibles
| Code | Erreur | Description |
|---|---|---|
| 400 | appId et secretKey requis | Champs manquants |
| 401 | Application non trouvée ou inactive | App ID invalide |
| 401 | Credentials invalides | Secret Key incorrecte |
POST /api/oauth/verify
Vérifier si un access token est valide et non expiré.
Request
{
"access_token": "a1b2c3d4..."
}
Response (200 OK)
{
"valid": true,
"appId": "590551",
"appName": "Mon Application",
"expiresAt": "2025-10-16T20:00:00.000Z",
"expires_in": 850000
}
POST /api/oauth/verify-user
Vérifier un token utilisateur avec votre access token d'application.
Request
{
"access_token": "a1b2c3d4...",
"user_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Response (200 OK)
{
"valid": true,
"user": {
"id": "507f1f77bcf86cd799439011",
"username": "johndoe",
"firstName": "John",
"lastName": "Doe",
"email": "[email protected]",
"profilePicture": "data:image/jpeg;base64,...",
"createdAt": "2025-01-01T00:00:00.000Z"
},
"app": {
"appId": "590551",
"name": "Mon Application"
}
}
POST /api/oauth/revoke
Révoquer un access token immédiatement.
Request
{
"access_token": "token_a_revoquer"
}
Response (200 OK)
{
"message": "Token révoqué avec succès"
}
Les access tokens ont une durée de vie de 10 jours. Pensez à les renouveler automatiquement avant expiration dans votre application.
API Authentification
Endpoints d'inscription et connexion
API Authentification - Référence complète
Endpoints pour l'inscription, la connexion et la gestion des sessions utilisateurs.
POST /api/auth/register
Créer un nouveau compte utilisateur.
Paramètres
| Champ | Type | Requis | Description |
|---|---|---|---|
| firstName | string | Prénom de l'utilisateur | |
| lastName | string | Nom de famille | |
| username | string | Nom d'utilisateur unique (min 3 caractères, lettres/chiffres/_) | |
| string | Adresse email (format valide) | ||
| password | string | Mot de passe (min 6 caractères) | |
| dateOfBirth | date | Date de naissance (format: YYYY-MM-DD) | |
| profilePicture | file | Image de profil (max 5MB, jpeg/png/gif/webp) | |
| hcaptchaToken | string | Token hCaptcha de validation |
Request (multipart/form-data ou JSON)
const response = await fetch('https://auth.codealuxz.fr/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
firstName: 'John',
lastName: 'Doe',
username: 'johndoe',
email: '[email protected]',
password: 'motdepasse123',
dateOfBirth: '1990-01-01',
hcaptchaToken: 'P1_eyJ0eXAiOiJKV1QiLCJhbGc...'
})
});
Response (201 Created)
{
"message": "Compte créé avec succès",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "507f1f77bcf86cd799439011",
"username": "johndoe",
"firstName": "John",
"lastName": "Doe",
"email": "[email protected]",
"dateOfBirth": "1990-01-01",
"createdAt": "2025-01-06T00:00:00.000Z"
}
}
POST /api/auth/login
Connexion utilisateur avec username/email et mot de passe.
Request
{
"identifier": "johndoe",
"password": "motdepasse123"
}
Response (200 OK)
{
"message": "Connexion réussie",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": { ... }
}
POST /api/auth/verify-token
Vérifier la validité d'un token utilisateur.
Request
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Response (200 OK)
{
"valid": true,
"user": { ... }
}
POST /api/auth/forgot-password
Demander un code de réinitialisation de mot de passe (envoyé par email).
Request
{
"identifier": "[email protected]"
}
Response (200 OK)
{
"message": "Code de réinitialisation envoyé par email",
"emailSent": true
}
POST /api/auth/reset-password
Réinitialiser le mot de passe avec le code reçu par email.
Request
{
"identifier": "[email protected]",
"code": "123456",
"newPassword": "nouveaumotdepasse"
}
Response (200 OK)
{
"message": "Mot de passe réinitialisé avec succès"
}
L'inscription est limitée à 1 compte par heure par IP pour prévenir les abus.
API Gestion Utilisateurs
Profils, sessions, et paramètres
Gestion des utilisateurs
Routes protégées pour gérer le profil et les sessions de l'utilisateur connecté.
Toutes ces routes nécessitent un token utilisateur valide dans le header Authorization: Bearer TOKEN
GET /api/auth/profile
Récupérer le profil complet de l'utilisateur connecté.
Request
const response = await fetch('https://auth.codealuxz.fr/api/auth/profile', {
headers: {
'Authorization': 'Bearer ' + userToken
}
});
Response (200 OK)
{
"user": {
"id": "507f1f77bcf86cd799439011",
"username": "johndoe",
"firstName": "John",
"lastName": "Doe",
"email": "[email protected]",
"dateOfBirth": "1990-01-01",
"profilePicture": "data:image/jpeg;base64,...",
"privacy": {
"hideFirstName": false,
"hideLastName": false,
"hideDateOfBirth": true,
"hideEmail": false
},
"createdAt": "2025-01-01T00:00:00.000Z"
}
}
PUT /api/auth/profile
Mettre à jour le profil utilisateur.
Paramètres (tous optionnels)
| Champ | Type | Description |
|---|---|---|
| firstName | string | Nouveau prénom |
| lastName | string | Nouveau nom |
| string | Nouvel email | |
| dateOfBirth | date | Nouvelle date de naissance |
| profilePicture | file/base64 | Nouvelle photo de profil |
| privacy | object | Paramètres de confidentialité |
GET /api/auth/sessions
Liste de toutes les sessions actives de l'utilisateur.
Response (200 OK)
{
"user": { ... },
"sessions": [
{
"deviceInfo": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
"lastActive": "2025-01-06T10:30:00.000Z",
"createdAt": "2025-01-05T08:00:00.000Z"
},
{
"deviceInfo": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0...)...",
"lastActive": "2025-01-06T09:15:00.000Z",
"createdAt": "2025-01-04T15:20:00.000Z"
}
],
"totalSessions": 2
}
POST /api/auth/logout
Déconnexion de la session actuelle uniquement.
Response (200 OK)
{
"message": "Déconnexion réussie"
}
POST /api/auth/logout-all
Déconnexion de TOUTES les sessions (tous les appareils).
Response (200 OK)
{
"message": "Déconnexion de toutes les sessions réussie"
}
Utilisez /logout-all si l'utilisateur pense que son compte a été compromis.
Exemples JavaScript/Node.js
Exemples complets en JavaScript
Exemples JavaScript/Node.js complets
Exemple 1 : Express.js avec middleware personnalisé
require('dotenv').config();
const express = require('express');
const fetch = require('node-fetch');
const app = express();
app.use(express.json());
// Cache global pour l'access token
class TokenManager {
constructor() {
this.token = null;
this.expiry = null;
}
async getToken() {
if (this.token && this.expiry && new Date() < new Date(this.expiry)) {
return this.token;
}
const res = await fetch('https://auth.codealuxz.fr/api/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
appId: process.env.CODEA_APP_ID,
secretKey: process.env.CODEA_SECRET_KEY
})
});
const data = await res.json();
this.token = data.access_token;
this.expiry = data.expires_at;
return this.token;
}
}
const tokenManager = new TokenManager();
// Middleware d'authentification
async function authenticate(req, res, next) {
try {
const userToken = req.headers.authorization?.replace('Bearer ', '');
if (!userToken) {
return res.status(401).json({ error: 'Token requis' });
}
const accessToken = await tokenManager.getToken();
const verifyRes = await fetch('https://auth.codealuxz.fr/api/oauth/verify-user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
access_token: accessToken,
user_token: userToken
})
});
const data = await verifyRes.json();
if (data.valid) {
req.user = data.user;
next();
} else {
res.status(401).json({ error: 'Token invalide' });
}
} catch (error) {
res.status(500).json({ error: 'Erreur serveur' });
}
}
// Routes
app.get('/api/dashboard', authenticate, (req, res) => {
res.json({
message: `Bienvenue ${req.user.username}`,
user: req.user
});
});
app.get('/api/protected', authenticate, (req, res) => {
res.json({
data: 'Données sensibles',
user: req.user.username
});
});
app.listen(3000, () => console.log('Serveur démarré sur :3000'));
Exemple 2 : Next.js API Route
// pages/api/user.js
let accessToken = null;
let tokenExpiry = null;
async function getAccessToken() {
if (accessToken && tokenExpiry && new Date() < new Date(tokenExpiry)) {
return accessToken;
}
const res = await fetch('https://auth.codealuxz.fr/api/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
appId: process.env.CODEA_APP_ID,
secretKey: process.env.CODEA_SECRET_KEY
})
});
const data = await res.json();
accessToken = data.access_token;
tokenExpiry = data.expires_at;
return accessToken;
}
export default async function handler(req, res) {
if (req.method !== 'GET') {
return res.status(405).json({ error: 'Method not allowed' });
}
const userToken = req.headers.authorization?.replace('Bearer ', '');
if (!userToken) {
return res.status(401).json({ error: 'Non authentifié' });
}
try {
const at = await getAccessToken();
const verifyRes = await fetch('https://auth.codealuxz.fr/api/oauth/verify-user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
access_token: at,
user_token: userToken
})
});
const data = await verifyRes.json();
if (data.valid) {
res.status(200).json({ user: data.user });
} else {
res.status(401).json({ error: 'Token invalide' });
}
} catch (error) {
res.status(500).json({ error: 'Erreur serveur' });
}
}
Exemple 3 : Vanilla JavaScript (Frontend)
// Classe pour gérer l'authentification
class AuthManager {
constructor(appId, secretKey) {
this.auth = new CodeaAuth({
appId,
secretKey,
onSuccess: this.handleSuccess.bind(this),
onError: this.handleError.bind(this)
});
this.userToken = localStorage.getItem('userToken');
this.user = null;
}
handleSuccess(result) {
this.userToken = result.token;
this.user = result.user;
localStorage.setItem('userToken', this.userToken);
localStorage.setItem('user', JSON.stringify(this.user));
this.onAuthChange(this.user);
}
handleError(error) {
console.error('Erreur auth:', error);
alert('Erreur : ' + error.message);
}
login() {
this.auth.login();
}
logout() {
this.userToken = null;
this.user = null;
localStorage.removeItem('userToken');
localStorage.removeItem('user');
this.onAuthChange(null);
}
async checkAuth() {
if (!this.userToken) return false;
try {
const res = await fetch('https://auth.codealuxz.fr/api/auth/verify-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: this.userToken })
});
const data = await res.json();
if (data.valid) {
this.user = data.user;
this.onAuthChange(this.user);
return true;
} else {
this.logout();
return false;
}
} catch (error) {
console.error('Erreur vérification:', error);
return false;
}
}
onAuthChange(user) {
// À implémenter par l'utilisateur
console.log('État auth changé:', user);
}
}
// Utilisation
const authManager = new AuthManager('590551', 'YOUR_SECRET');
authManager.onAuthChange = (user) => {
if (user) {
document.getElementById('login-btn').style.display = 'none';
document.getElementById('user-info').style.display = 'block';
document.getElementById('username').textContent = user.username;
} else {
document.getElementById('login-btn').style.display = 'block';
document.getElementById('user-info').style.display = 'none';
}
};
// Vérifier au chargement
authManager.checkAuth();
Exemples Python
Exemples complets en Python
Exemples Python complets
Exemple 1 : Flask avec décorateur personnalisé
import os
from datetime import datetime
from functools import wraps
from flask import Flask, request, jsonify
from dotenv import load_dotenv
import requests
load_dotenv()
app = Flask(__name__)
# Cache pour l'access token
class TokenManager:
def __init__(self):
self.token = None
self.expiry = None
def get_token(self):
if self.token and self.expiry:
if datetime.now() < datetime.fromisoformat(self.expiry.replace('Z', '+00:00')):
return self.token
response = requests.post(
f"{os.getenv('CODEA_AUTH_URL')}/api/oauth/token",
json={
'appId': os.getenv('CODEA_APP_ID'),
'secretKey': os.getenv('CODEA_SECRET_KEY')
}
)
data = response.json()
self.token = data['access_token']
self.expiry = data['expires_at']
return self.token
token_manager = TokenManager()
# Décorateur d'authentification
def require_auth(f):
@wraps(f)
def decorated_function(*args, **kwargs):
auth_header = request.headers.get('Authorization', '')
user_token = auth_header.replace('Bearer ', '')
if not user_token:
return jsonify({'error': 'Non authentifié'}), 401
try:
access_token = token_manager.get_token()
response = requests.post(
f"{os.getenv('CODEA_AUTH_URL')}/api/oauth/verify-user",
json={
'access_token': access_token,
'user_token': user_token
}
)
data = response.json()
if data.get('valid'):
request.user = data['user']
return f(*args, **kwargs)
else:
return jsonify({'error': 'Token invalide'}), 401
except Exception as e:
return jsonify({'error': str(e)}), 500
return decorated_function
# Routes protégées
@app.route('/api/dashboard')
@require_auth
def dashboard():
return jsonify({
'message': f"Bienvenue {request.user['username']}",
'user': request.user
})
@app.route('/api/protected')
@require_auth
def protected():
return jsonify({
'data': 'Données sensibles',
'user': request.user['username']
})
if __name__ == '__main__':
app.run(debug=True, port=3000)
Exemple 2 : Django View
import os
import requests
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import csrf_exempt
# Cache global
_access_token = None
_token_expiry = None
def get_access_token():
global _access_token, _token_expiry
if _access_token and _token_expiry:
from datetime import datetime
if datetime.now() < datetime.fromisoformat(_token_expiry.replace('Z', '+00:00')):
return _access_token
response = requests.post(
'https://auth.codealuxz.fr/api/oauth/token',
json={
'appId': os.getenv('CODEA_APP_ID'),
'secretKey': os.getenv('CODEA_SECRET_KEY')
}
)
data = response.json()
_access_token = data['access_token']
_token_expiry = data['expires_at']
return _access_token
def verify_user_token(user_token):
access_token = get_access_token()
response = requests.post(
'https://auth.codealuxz.fr/api/oauth/verify-user',
json={
'access_token': access_token,
'user_token': user_token
}
)
return response.json()
@csrf_exempt
@require_http_methods(['POST'])
def verify_user(request):
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
user_token = auth_header.replace('Bearer ', '')
if not user_token:
return JsonResponse({'error': 'Token requis'}, status=401)
try:
data = verify_user_token(user_token)
if data.get('valid'):
return JsonResponse({
'success': True,
'user': data['user']
})
else:
return JsonResponse({
'success': False,
'error': data.get('error')
}, status=401)
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
Exemple 3 : FastAPI
import os
from fastapi import FastAPI, Depends, HTTPException, Header
from pydantic import BaseModel
import requests
app = FastAPI()
# Cache
_access_token = None
_token_expiry = None
async def get_access_token():
global _access_token, _token_expiry
if _access_token and _token_expiry:
from datetime import datetime
if datetime.now() < datetime.fromisoformat(_token_expiry.replace('Z', '+00:00')):
return _access_token
response = requests.post(
'https://auth.codealuxz.fr/api/oauth/token',
json={
'appId': os.getenv('CODEA_APP_ID'),
'secretKey': os.getenv('CODEA_SECRET_KEY')
}
)
data = response.json()
_access_token = data['access_token']
_token_expiry = data['expires_at']
return _access_token
async def get_current_user(authorization: str = Header(None)):
if not authorization or not authorization.startswith('Bearer '):
raise HTTPException(status_code=401, detail='Non authentifié')
user_token = authorization.replace('Bearer ', '')
access_token = await get_access_token()
response = requests.post(
'https://auth.codealuxz.fr/api/oauth/verify-user',
json={
'access_token': access_token,
'user_token': user_token
}
)
data = response.json()
if not data.get('valid'):
raise HTTPException(status_code=401, detail='Token invalide')
return data['user']
@app.get('/api/dashboard')
async def dashboard(user: dict = Depends(get_current_user)):
return {
'message': f"Bienvenue {user['username']}",
'user': user
}
@app.get('/api/protected')
async def protected(user: dict = Depends(get_current_user)):
return {
'data': 'Données sensibles',
'user': user['username']
}
Exemples PHP
Exemples complets en PHP
Exemples PHP complets
Exemple 1 : PHP avec fonction helper
<?php
require 'vendor/autoload.php';
use Dotenv\Dotenv;
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
// Cache pour l'access token (utiliser Redis/Memcached en production)
$cacheFile = __DIR__ . '/cache/codea_token.json';
function getAccessToken() {
global $cacheFile;
// Vérifier le cache
if (file_exists($cacheFile)) {
$cache = json_decode(file_get_contents($cacheFile), true);
if (strtotime($cache['expires_at']) > time()) {
return $cache['access_token'];
}
}
// Générer un nouveau token
$ch = curl_init($_ENV['CODEA_AUTH_URL'] . '/api/oauth/token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'appId' => $_ENV['CODEA_APP_ID'],
'secretKey' => $_ENV['CODEA_SECRET_KEY']
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception('Erreur génération token');
}
$data = json_decode($response, true);
// Sauvegarder en cache
if (!is_dir(dirname($cacheFile))) {
mkdir(dirname($cacheFile), 0755, true);
}
file_put_contents($cacheFile, json_encode($data));
return $data['access_token'];
}
function verifyUserToken($userToken) {
$accessToken = getAccessToken();
$ch = curl_init($_ENV['CODEA_AUTH_URL'] . '/api/oauth/verify-user');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'access_token' => $accessToken,
'user_token' => $userToken
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
function requireAuth() {
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
$userToken = str_replace('Bearer ', '', $authHeader);
if (empty($userToken)) {
http_response_code(401);
echo json_encode(['error' => 'Non authentifié']);
exit;
}
$result = verifyUserToken($userToken);
if (!$result['valid']) {
http_response_code(401);
echo json_encode(['error' => $result['error'] ?? 'Token invalide']);
exit;
}
return $result['user'];
}
// Utilisation
header('Content-Type: application/json');
$method = $_SERVER['REQUEST_METHOD'];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if ($method === 'GET' && $path === '/api/dashboard') {
$user = requireAuth();
echo json_encode([
'message' => "Bienvenue {$user['username']}",
'user' => $user
]);
exit;
}
if ($method === 'GET' && $path === '/api/protected') {
$user = requireAuth();
echo json_encode([
'data' => 'Données sensibles',
'user' => $user['username']
]);
exit;
}
http_response_code(404);
echo json_encode(['error' => 'Route non trouvée']);
?>
Exemple 2 : Laravel Middleware
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
class CodeaAuthMiddleware
{
public function handle(Request $request, Closure $next)
{
$userToken = $request->bearerToken();
if (!$userToken) {
return response()->json(['error' => 'Non authentifié'], 401);
}
$user = $this->verifyUserToken($userToken);
if (!$user) {
return response()->json(['error' => 'Token invalide'], 401);
}
$request->merge(['user' => $user]);
return $next($request);
}
private function getAccessToken()
{
return Cache::remember('codea_access_token', 864000, function () {
$response = Http::post(env('CODEA_AUTH_URL') . '/api/oauth/token', [
'appId' => env('CODEA_APP_ID'),
'secretKey' => env('CODEA_SECRET_KEY')
]);
if ($response->successful()) {
return $response->json()['access_token'];
}
throw new \Exception('Erreur génération token');
});
}
private function verifyUserToken($userToken)
{
$accessToken = $this->getAccessToken();
$response = Http::post(env('CODEA_AUTH_URL') . '/api/oauth/verify-user', [
'access_token' => $accessToken,
'user_token' => $userToken
]);
if ($response->successful() && $response->json()['valid']) {
return $response->json()['user'];
}
return null;
}
}
?>
Bonnes pratiques de sécurité
Sécurisez votre intégration
Bonnes pratiques de sécurité
À FAIRE impérativement
1Protéger votre Secret Key
- Stocker dans un fichier
.env(jamais dans le code) - Ajouter
.envau.gitignore - Utiliser des variables d'environnement sur le serveur
- Ne jamais commit dans Git
- Ne jamais exposer côté client/frontend
2Utiliser HTTPS uniquement
- Obligatoire en production
- Certificat SSL/TLS valide
- Rediriger HTTP → HTTPS automatiquement
3Implémenter OAuth côté backend
- Utiliser des access tokens temporaires (10 jours)
- Vérifier les tokens utilisateurs côté serveur
- Ne jamais faire confiance au frontend seul
4Valider les données utilisateur
- Toujours vérifier les tokens avant d'autoriser l'accès
- Valider les permissions/rôles si nécessaire
- Vérifier que le token n'a pas expiré
5Gérer les sessions proprement
- Stocker les tokens dans
localStorageou cookies sécurisés - Nettoyer les tokens à la déconnexion
- Vérifier les tokens au chargement de l'application
À NE JAMAIS FAIRE
| Mauvaise pratique | Solution |
|---|---|
| Hardcoder Secret Key dans le code | Utiliser process.env.SECRET_KEY |
| Exposer Secret Key côté client | Utiliser OAuth avec access tokens |
| Commit .env dans Git | Ajouter .env au .gitignore |
| Faire confiance au frontend | Toujours vérifier côté backend |
| Utiliser HTTP en production | Obligatoire HTTPS |
| Stocker passwords en clair | Codea Auth les hash avec bcrypt |
Exemple de configuration sécurisée
# Fichier .env (à ajouter au .gitignore)
CODEA_APP_ID=590551
CODEA_SECRET_KEY=00a75ea7f31ee220ea6414d69de7b85b8113b65276e62f74f0403930f333bdb8
CODEA_AUTH_URL=https://auth.codealuxz.fr
# Autres variables
NODE_ENV=production
PORT=3000
# Toujours ignorer ces fichiers
.env
.env.local
.env.production
.env.*.local
node_modules/
dist/
build/
Si votre Secret Key a été compromise (commit Git, exposure publique), créez immédiatement une nouvelle application dans votre dashboard et mettez à jour vos credentials.
Checklist de sécurité
- Secret Key dans .env (pas dans le code)
- .env dans .gitignore
- HTTPS activé en production
- OAuth avec access tokens temporaires
- Validation tokens côté backend
- Certificat SSL valide
- CORS configuré correctement
- Rate limiting activé (déjà inclus dans Codea Auth)
- hCaptcha activé sur l'inscription
- Sessions nettoyées à la déconnexion
Anti-spam & hCaptcha
Protection contre les abus
Protection anti-spam & hCaptcha
Protections intégrées
Codea Auth inclut plusieurs mécanismes de protection contre les abus :
1hCaptcha (99.9% Passive)
- Protection automatique lors de l'inscription
- Mode passif : invisible pour 99.9% des utilisateurs légitimes
- Challenge uniquement pour les comportements suspects
- Site Key :
576eee9a-3966-44da-8972-d77627e67e7e
2Rate Limiting (1 compte/heure/IP)
- Limite stricte : 1 inscription par heure par adresse IP
- Appliqué APRÈS validation du hCaptcha
- Basé sur l'IP réelle (compatible avec proxies/CDN)
- Message d'erreur clair pour l'utilisateur
3Validation des données
- Username : min 3 caractères, lettres/chiffres/underscore uniquement
- Email : format valide vérifié
- Password : minimum 6 caractères
- Unicité : username et email doivent être uniques
Configuration hCaptcha
Frontend - Intégration automatique
Le formulaire d'inscription sur /register.html inclut déjà hCaptcha :
<!-- Déjà inclus dans le formulaire d'inscription -->
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
<form id="registerForm">
<!-- ... autres champs ... -->
<div class="h-captcha" data-sitekey="576eee9a-3966-44da-8972-d77627e67e7e"></div>
<button type="submit">S'inscrire</button>
</form>
Backend - Vérification automatique
Le backend vérifie automatiquement le token hCaptcha :
// Middleware hCaptcha (déjà intégré)
const verifyHCaptcha = async (req, res, next) => {
const hcaptchaToken = req.body.hcaptchaToken;
if (!hcaptchaToken) {
return res.status(400).json({
error: 'Vérification hCaptcha requise'
});
}
const verification = await verify(
process.env.HCAPTCHA_SECRET_KEY,
hcaptchaToken
);
if (!verification.success) {
return res.status(400).json({
error: 'Échec de la vérification hCaptcha'
});
}
next();
};
Statistiques de protection
| Protection | Type | Efficacité |
|---|---|---|
| hCaptcha | Bot detection | 99.9% |
| Rate Limiting | Spam accounts | 100% |
| Email validation | Format invalide | 100% |
| Username uniqueness | Duplicates | 100% |
Gestion des erreurs
Erreur hCaptcha
{
"error": "Vérification hCaptcha requise"
}
Erreur Rate Limiting
{
"error": "Limite d'inscription atteinte. Réessayez dans 1 heure."
}
Toutes ces protections sont actives par défaut. Vous n'avez rien à configurer !
Dépannage
Solutions aux problèmes courants
Guide de dépannage
Erreur : "Token invalide"
Causes possibles
- Le token a expiré (session fermée)
- Le token a été révoqué (déconnexion)
- Format de token incorrect
- Utilisateur supprimé
Solution
// Vérifier et nettoyer si invalide
const token = localStorage.getItem('userToken');
const res = await fetch('https://auth.codealuxz.fr/api/auth/verify-token', {
method: 'POST',
body: JSON.stringify({ token })
});
const data = await res.json();
if (!data.valid) {
// Token invalide, nettoyer et redemander connexion
localStorage.clear();
window.location.href = '/login';
}
Erreur : "Application non trouvée"
Causes possibles
- App ID incorrect
- Application désactivée
- Application supprimée
Solution
- Vérifier l'App ID dans votre dashboard
- Vérifier que l'application est active
- Recréer l'application si nécessaire
Erreur : "Credentials invalides"
Causes possibles
- Secret Key incorrecte
- Mauvaise variable d'environnement
Solution
# Vérifier les variables d'environnement
echo $CODEA_APP_ID
echo $CODEA_SECRET_KEY
# S'assurer que .env est chargé
node -e "require('dotenv').config(); console.log(process.env.CODEA_SECRET_KEY)"
Erreur : "Access token expiré"
Causes possibles
- Token a dépassé 10 jours
- Token révoqué manuellement
Solution
Régénérer automatiquement :
let cachedToken = null;
let tokenExpiry = null;
async function getValidToken() {
// Vérifier si le token est encore valide
if (cachedToken && tokenExpiry && new Date() < new Date(tokenExpiry)) {
return cachedToken;
}
// Régénérer
const res = await fetch('https://auth.codealuxz.fr/api/oauth/token', {
method: 'POST',
body: JSON.stringify({ appId, secretKey })
});
const data = await res.json();
cachedToken = data.access_token;
tokenExpiry = data.expires_at;
return cachedToken;
}
La popup ne s'ouvre pas
Causes possibles
- Popups bloquées par le navigateur
- Script SDK non chargé
- Erreur JavaScript
Solution
- Vérifier la console navigateur (F12)
- Autoriser les popups pour votre site
- Vérifier que le SDK est bien chargé :
<script src="https://auth.codealuxz.fr/codea-auth-sdk.js">
Erreur CORS
Causes possibles
- Requête depuis un domaine non autorisé
- Headers manquants
Solution
Codea Auth autorise tous les origins. Si vous rencontrez une erreur CORS, vérifiez votre configuration backend :
// Express.js
const cors = require('cors');
app.use(cors());
// Ou spécifique
app.use(cors({
origin: 'https://mon-site.com',
credentials: true
}));
Support
Si votre problème persiste :
- � Email : [email protected]
- Documentation complète : docs.html
- � Community Discord : (lien à venir)
FAQ
Questions fréquemment posées
Questions fréquemment posées (FAQ)
Tarification
Q : Codea Auth est-il gratuit ?
Oui, complètement gratuit et sans limite d'utilisateurs ou d'applications.
Q : Y a-t-il des frais cachés ou des limitations ?
Non, aucun frais caché. Le service est totalement gratuit.
Sécurité
Q : Les mots de passe sont-ils sécurisés ?
Oui, tous les mots de passe sont hachés avec bcrypt (10 salt rounds) avant d'être stockés.
Q : Mes données utilisateur sont-elles partagées ?
Non, jamais. Vos données restent privées et ne sont jamais vendues ou partagées.
Q : Puis-je exporter les données de mes utilisateurs ?
Actuellement non, mais cette fonctionnalité est prévue prochainement.
Durée de vie
Q : Combien de temps dure un token utilisateur ?
Illimité jusqu'à déconnexion manuelle.
Q : Combien de temps dure un access token (OAuth) ?
10 jours. Il se renouvelle automatiquement si vous utilisez le SDK.
Q : Que se passe-t-il si mon token expire ?
Pour les access tokens : régénérez-en un nouveau. Pour les tokens utilisateurs : l'utilisateur doit se reconnecter.
Personnalisation
Q : Puis-je personnaliser l'interface de connexion ?
L'interface de la popup est standardisée pour l'instant. Personnalisation prévue dans une future version.
Q : Puis-je ajouter mon logo dans la popup ?
Pas encore disponible, mais en développement.
Compatibilité
Q : Codea Auth fonctionne-t-il sur mobile ?
Oui, l'interface est responsive et fonctionne sur tous les appareils.
Q : Puis-je utiliser Codea Auth dans une application mobile native ?
Oui, utilisez l'API REST directement depuis votre app (iOS, Android, React Native, etc.)
Q : Quels navigateurs sont supportés ?
Tous les navigateurs modernes (Chrome, Firefox, Safari, Edge). IE11 non supporté.
Technique
Q : Puis-je avoir plusieurs applications ?
Oui, créez autant d'applications que vous voulez depuis votre dashboard.
Q : Un utilisateur peut-il se connecter à plusieurs de mes applications ?
Oui, un compte Codea Auth peut autoriser plusieurs applications.
Q : Puis-je utiliser Codea Auth avec React/Vue/Angular ?
Oui, le SDK JavaScript fonctionne avec tous les frameworks. Voir les exemples.
Q : Dois-je avoir un backend ?
Pas obligatoire, mais fortement recommandé pour la sécurité (OAuth).
� Récupération de mot de passe
Q : Que faire si un utilisateur perd son mot de passe ?
Utilisez la fonction "Mot de passe oublié" qui envoie un code par email (valide 15 minutes).
Q : Les emails fonctionnent-ils ?
Oui, les emails de récupération et de bienvenue sont envoyés automatiquement.
Stockage et sauvegarde
Q : Où sont stockées les données ?
� MongoDB avec réplication pour la haute disponibilité.
Q : Les données sont-elles sauvegardées ?
Oui, sauvegardes automatiques quotidiennes.
Q : Quelle est la disponibilité du service ?
Disponibilité cible de 99.9% (SLA).
Performance
Q : Y a-t-il une limite d'utilisateurs ?
Non, aucune limite.
Q : Y a-t-il une limite de requêtes par seconde ?
Rate limiting uniquement sur l'inscription (1/heure/IP). Les autres endpoints sont illimités.
Développement
Q : Puis-je tester en local (localhost) ?
Oui, Codea Auth fonctionne parfaitement en développement local.
Q : Y a-t-il un environnement de staging ?
Créez simplement une application de test dans votre dashboard.
Support
Q : Comment obtenir de l'aide ?
� Email : [email protected]
Documentation : docs.html
Troubleshooting : Guide de dépannage
Contactez-nous à [email protected] et nous ajouterons la réponse à cette FAQ !