Back to flin
flin

OAuth2 et authentification sociale

Comment FLIN fournit des fonctions OAuth2 intégrées pour Google, GitHub, Discord, Apple, LinkedIn et Telegram -- flux PKCE, validation d'état et création d'utilisateurs dans un schéma standardisé.

Thales & Claude | March 30, 2026 8 min flin
EN/ FR/ ES
flinoauth2social-authgooglegithub

La connexion sociale est attendue dans chaque application web moderne. Les utilisateurs veulent cliquer sur « Se connecter avec Google » et être authentifiés en quelques secondes, sans créer un autre mot de passe. Les développeurs veulent offrir cette possibilité, mais l'implémentation est notoirement complexe : codes d'autorisation OAuth2, URI de redirection, paramètres d'état, vérificateurs de code PKCE, échanges de jetons et particularités propres à chaque fournisseur.

Dans une application Node.js, implémenter Google OAuth nécessite passport, passport-google-oauth20, la configuration de session, des routes de rappel et une configuration spécifique au fournisseur. GitHub nécessite passport-github2 avec des portées différentes et des champs de profil utilisateur différents. Chaque fournisseur est un paquet distinct avec sa propre API.

FLIN fournit des fonctions intégrées pour six fournisseurs OAuth : Google, GitHub, Discord, Apple, LinkedIn et Telegram. Chaque fournisseur suit le même schéma en trois étapes : générer l'URL d'authentification, gérer le rappel, et créer ou connecter l'utilisateur.

Le schéma universel

Chaque fournisseur OAuth dans FLIN suit le même flux :

Étape 1 : Générer l'URL d'authentification (page de connexion)
  auth = auth_PROVIDER_login(baseUrl)
  session.PROVIDERState = auth.state
  <a href={auth.url}>Continuer avec PROVIDER</a>

Étape 2 : Gérer le rappel (page de rappel)
  result = auth_PROVIDER_callback(query.code, query.state, session.PROVIDERState)
  session.PROVIDERState = none

Étape 3 : Trouver ou créer l'utilisateur
  if result.ok {
      existing = User.where(email == result.user.email).first
      if existing != none { connecter }
      else { créer un nouvel utilisateur }
  }

Les noms de fonctions, le schéma de stockage en session et la logique de recherche ou création sont identiques pour tous les fournisseurs. Apprenez-en un, et vous les connaissez tous.

Google OAuth avec PKCE

Google est le fournisseur OAuth le plus couramment utilisé. FLIN l'implémente avec PKCE (Proof Key for Code Exchange), l'extension de sécurité recommandée pour les clients publics :

flin// app/login.flin -- Étape 1 : Générer l'URL d'authentification

baseUrl = env("BASE_URL") || "http://localhost:3000"
googleAuth = auth_google_login(baseUrl)

// Stocker l'état et le vérificateur PKCE en session
session.googleState = googleAuth.state
session.googleRedirectUri = googleAuth.redirect_uri
session.googleCodeVerifier = googleAuth.code_verifier

// Afficher le bouton de connexion
<a href={googleAuth.url} class="social-btn">
    Continuer avec Google
</a>

La fonction auth_google_login() génère : - Un paramètre state aléatoire (protection CSRF) - Un code_verifier et un code_challenge (PKCE) - L'URL d'autorisation complète avec les portées, l'URI de redirection et le type de réponse

flin// app/auth/google/callback.flin -- Étape 2 : Gérer le rappel

layout = "auth"

result = auth_google_callback(query.code, query.state, session.googleState)
session.googleState = none

authOk = false

fn processGoogleAuth() {
    if result.ok {
        existing = User.where(email == result.user.email && role == "User").first
        if existing != none {
            session.user = existing.email
            session.userName = existing.name || result.user.name
            session.userId = to_text(existing.id)
        } else {
            newUser = User {
                email: result.user.email,
                name: result.user.name,
                provider: "Google",
                providerId: to_text(result.user.id),
                avatar: result.user.avatar || "",
                emailVerified: true
            }
            save newUser
            session.user = newUser.email
            session.userName = newUser.name
            session.userId = to_text(newUser.id)
        }
        authOk = true
    }
}
processGoogleAuth()

{if authOk}
    <h2>Bienvenue !</h2>
    <script>setTimeout(function() { window.location.href = "/tasks"; }, 1000);</script>
{else}
    <h2>Échec de l'authentification</h2>
    <a href="/login">Réessayer</a>
{/if}

GitHub OAuth

GitHub suit le même schéma avec deux différences clés : la gestion des e-mails privés et la disponibilité du nom d'utilisateur.

flin// app/login.flin
githubAuth = auth_github_login(baseUrl)
session.githubState = githubAuth.state
session.githubRedirectUri = githubAuth.redirect_uri
session.githubCodeVerifier = githubAuth.code_verifier

<a href={githubAuth.url} class="social-btn">
    Continuer avec GitHub
</a>

Les utilisateurs GitHub peuvent masquer leur adresse e-mail. Le rappel doit gérer cela :

flin// app/auth/github/callback.flin

result = auth_github_callback(query.code, query.state, session.githubState)
session.githubState = none

fn processGithubAuth() {
    if result.ok {
        // Gérer l'e-mail privé
        githubEmail = result.user.email || to_text(result.user.id) + "@github.flin"
        githubName = result.user.name || result.user.login || "GitHub User"

        existing = User.where(email == githubEmail && role == "User").first
        if existing != none {
            session.user = existing.email
            session.userName = existing.name || githubName
            session.userId = to_text(existing.id)
        } else {
            newUser = User {
                email: githubEmail,
                name: githubName,
                provider: "GitHub",
                providerId: to_text(result.user.id),
                avatar: result.user.avatar_url || "",
                emailVerified: result.user.email != none
            }
            save newUser
            session.user = newUser.email
            session.userName = newUser.name
            session.userId = to_text(newUser.id)
        }
        authOk = true
    }
}

L'e-mail de secours {id}@github.flin garantit que les utilisateurs sans e-mail public peuvent toujours s'inscrire. Le drapeau emailVerified est défini selon que nous avons obtenu un vrai e-mail de GitHub.

Discord OAuth

Discord utilise OAuth2 standard avec les portées identify email :

flin// Configuration
discordAuth = auth_discord_login(baseUrl)
session.discordState = discordAuth.state
session.discordRedirectUri = discordAuth.redirect_uri

<a href={discordAuth.url} class="social-btn">
    Continuer avec Discord
</a>

// Rappel
result = auth_discord_callback(query.code, query.state, session.discordState)
session.discordState = none
// ... même schéma de recherche ou création

Apple Sign-In

Apple Sign-In a des exigences uniques : il envoie les données utilisateur uniquement lors de la première authentification, utilise un rappel POST au lieu de GET, et nécessite un secret client généré à partir d'une clé privée.

flin// Configuration
appleAuth = auth_apple_login(baseUrl)
session.appleState = appleAuth.state

<a href={appleAuth.url} class="social-btn">
    Continuer avec Apple
</a>

// Rappel (gère le POST d'Apple)
result = auth_apple_callback(body.code, body.state, session.appleState)

FLIN gère les particularités d'Apple en interne. Le développeur utilise le même schéma que pour tous les autres fournisseurs.

LinkedIn OAuth

LinkedIn utilise OAuth 2.0 avec les portées openid profile email :

flinlinkedinAuth = auth_linkedin_login(baseUrl)
session.linkedinState = linkedinAuth.state

<a href={linkedinAuth.url} class="social-btn">
    Continuer avec LinkedIn
</a>

// Rappel
result = auth_linkedin_callback(query.code, query.state, session.linkedinState)

Widget de connexion Telegram

Telegram utilise un mécanisme différent : un widget de connexion JavaScript qui envoie un hash signé à votre URL de rappel. FLIN vérifie le hash en utilisant votre jeton de bot :

flin// Rappel (Telegram envoie les données comme paramètres de requête)
result = auth_telegram_callback(query)

fn processTelegramAuth() {
    if result.ok {
        telegramEmail = result.user.email || to_text(result.user.id) + "@telegram.flin"
        // ... même schéma de recherche ou création
    }
}

L'objet résultat OAuth

Chaque fonction auth_*_callback() retourne la même structure :

flin// Succès
result.ok              // true
result.user.id         // ID utilisateur du fournisseur
result.user.email      // E-mail (peut être none pour certains fournisseurs)
result.user.name       // Nom affiché
result.user.avatar     // URL de la photo de profil
result.user.provider   // "google", "github", etc.

// Échec
result.ok              // false
result.error           // Description de l'erreur

Cette interface cohérente signifie que la logique de recherche ou création est identique pour chaque fournisseur. Les seules différences sont dans les noms des variables de session et la configuration de la page de connexion.

Variables d'environnement

Chaque fournisseur nécessite des identifiants depuis sa console développeur :

# .env
GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxx

GITHUB_CLIENT_ID=xxx
GITHUB_CLIENT_SECRET=xxx

DISCORD_CLIENT_ID=xxx
DISCORD_CLIENT_SECRET=xxx

APPLE_CLIENT_ID=com.example.app
APPLE_TEAM_ID=xxx
APPLE_KEY_ID=xxx
APPLE_PRIVATE_KEY_PATH=.secrets/apple-auth-key.p8

LINKEDIN_CLIENT_ID=xxx
LINKEDIN_CLIENT_SECRET=xxx

TELEGRAM_BOT_TOKEN=xxx:xxx

Si les identifiants d'un fournisseur ne sont pas configurés, la fonction auth_*_login() retourne none et le bouton de connexion doit être masqué :

flingoogleAuth = auth_google_login(baseUrl)

{if googleAuth != none}
    <a href={googleAuth.url}>Continuer avec Google</a>
{/if}

Sécurité : état et PKCE

Chaque flux OAuth dans FLIN est protégé par deux mécanismes :

Paramètre d'état. Une chaîne aléatoire stockée en session et incluse dans l'URL d'autorisation. Le rappel vérifie que l'état dans la réponse correspond à l'état en session. Cela empêche les attaques CSRF où un attaquant incite un utilisateur à s'authentifier avec le compte de l'attaquant.

PKCE (Proof Key for Code Exchange). Un vérificateur de code aléatoire est généré et son hash SHA-256 (le défi de code) est envoyé avec la demande d'autorisation. Lors de l'échange du code d'autorisation contre des jetons, le vérificateur de code original est envoyé. Le serveur d'autorisation vérifie que le hash correspond. Cela empêche les attaques d'interception de code d'autorisation.

rustfn generate_pkce() -> (String, String) {
    let verifier = generate_random_string(64);
    let challenge = base64url_encode(&sha256(verifier.as_bytes()));
    (verifier, challenge)
}

Les deux protections sont générées et vérifiées automatiquement par les fonctions auth_<em>_login() et auth_</em>_callback(). Le développeur stocke l'état et le vérificateur en session mais n'a jamais besoin de comprendre pourquoi.

Pourquoi l'OAuth intégré est important

OAuth2 est un standard, mais les détails d'implémentation varient énormément d'un fournisseur à l'autre. Google exige PKCE. Apple envoie un POST. GitHub peut ne pas retourner d'e-mail. Discord utilise des noms de portées différents. LinkedIn a déprécié plusieurs versions d'API.

En absorbant ces différences dans des fonctions intégrées, FLIN garantit que : 1. Chaque implémentation OAuth suit correctement le standard. 2. Les mesures de sécurité (état, PKCE) sont toujours appliquées. 3. Les particularités des fournisseurs sont gérées en interne. 4. L'interface développeur est cohérente pour tous les fournisseurs.

L'alternative -- installer une bibliothèque séparée pour chaque fournisseur, configurer chacune différemment, et espérer qu'elles gèrent toutes correctement la sécurité -- est exactement le type de complexité que FLIN a été conçu pour éliminer.

Dans le prochain article, nous couvrons l'authentification WhatsApp OTP -- la méthode d'authentification conçue spécifiquement pour le marché africain où l'identité basée sur le téléphone est plus courante que l'e-mail.


Ceci est la partie 111 de la série « Comment nous avons construit FLIN », documentant comment un CEO à Abidjan et un CTO IA ont conçu et construit un langage de programmation à partir de zéro.

Navigation de la série : - [110] Authentification à deux facteurs (TOTP) - [111] OAuth2 et authentification sociale (vous êtes ici) - [112] Authentification WhatsApp OTP pour l'Afrique - [113] Validateurs de corps de requête

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles