

Discover more from Ruby Biscuit
Utiliser la session de Rails sur un site externe grâce aux CORS
Aujourd'hui vous allez apprendre que CORS n'est pas qu'une région de France, comment configurer le cookie de session de Rails pour qu’il fonctionne sur plusieurs domaines, comment modifier les règles CORS pour autoriser le téléchargement de ressources depuis une origine externe et comment utiliser fetch et un peu de JS pour afficher des données personnalisées sur un site statique.
Pas de panique, on vous explique tout.
Au programme :
Utiliser la session de Rails sur un site externe grâce aux CORS par Ismael
Le job de tes rêves !
Le défi
Bonjour et bienvenue sur la 11ème édition de Ruby Biscuit.
Vous êtes maintenant 216 abonnés 🥳 Si vous n’êtes pas déjà inscrit :
Utiliser la session de Rails sur un site externe grâce aux CORS
Chez Capsens, nous séparons systématiquement la logique plateforme (core business) et les pages marketing accessibles publiquement. Ainsi, nous réalisons en général deux sites web :
Une plateforme en Ruby on Rails, dont le but est d'être sécurisée, efficiente et robuste grâce à de nombreuses étapes de vérifications avant qu'une modification ne soit déployée en production (pull request avec review, CI, CD, vérification que les dépendances sont exemptes de CVE et enfin une double vérification humaine)
Un site vitrine statique (ou partiellement statique) en NextJs, qui est dédié à l'acquisition. Accessible publiquement, son but est d'être sexy, facile à modifier, très rapide et optimisé pour les moteurs de recherche (SEO).
L'avantage de séparer les logiques est que le site vitrine statique (hello la Jamstack) permet d'absorber une quantité immense de trafic, puisqu'il n'y a pas de serveur derrière. C'est donc très pratique lors d'un passage à la télévision, car une grande partie des visiteurs s'informe sur le site vitrine sans aller jusqu'à la plateforme. La vitrine fait donc tampon.
Nous ne sommes vraiment pas les seuls, ni les premiers à adopter cette logique. Voici quelques exemples :
Qonto : la plateforme sur app.qonto.com et la vitrine sur www.qonto.com,
October : la plateforme sur app.october.eu et la vitrine sur fr.october.eu,
Pennylane : la plateforme sur app.pennylane.com et la vitrine sur www.pennylane.com,
Skello : la plateforme sur app.skello.io et la vitrine sur www.skello.io.
Cette séparation des logiques a cependant des inconvénients. Il peut arriver que l'on soit tenté d'afficher sur la page d'accueil de la vitrine si l'utilisateur est connecté et quelques unes de ses propriétés (prénom, nom, etc). Cela permet de réduire la frontière entre le site vitrine et la plateforme, afin de proposer une navigation plus fluide et une transition plus douce lorsqu'il bascule de la vitrine à la plateforme.
Aujourd'hui, nous allons explorer une solution qui permet d'utiliser la session Ruby on Rails depuis une autre application de manière sécurisée. Cette solution peut être appliquée à n'importe quel CMS ou à tout site web statique (comme NextJs).
Nous allons :
Créer une route JSON authentifiée,
Faire fonctionner notre cookie de session sur plusieurs domaines,
Configurer les règles Cross-Origin Resource Sharing (CORS),
Corriger l'erreur
invalid host
,Ajouter un script JS pour récupérer la donnée,
Bonus : utiliser le stockage local pour la mise en cache des données.
Pour pouvoir suivre ce tutoriel, vous aurez besoin d'une application Rails avec Devise ou tout autre système d'authentification basé sur les cookies.
Vous aurez également besoin d'un site web statique séparé. Nous utiliserons une page HTML basique mais n'importe quel CMS / framework fonctionnerait.
Créer une route JSON authentifiée
Premièrement, nous allons créer la route /users/me
qui sera par défaut au format JSON et pointera vers l'action users/me#show
:
Ensuite, nous devons créer notre Users::MeController
composé de l'unique action show :
Enfin, nous ajouterons un test de requête simple :
Faire fonctionner notre cookie de session sur plusieurs domaines
Nous devons maintenant nous assurer que notre session Rails est partagée par tous les sous-domaines, afin qu'elle puisse être envoyée par notre site web statique lorsqu'il fera une requête à notre application Rails.
Pour cela nous aurons besoin de créer l'initializer session_store.rb
:
Notez que lorsque vous déploierez ces changements en environnement de production, tous vos utilisateurs seront automatiquement déconnectés. Assurez-vous donc de le faire à un moment où vous n'avez pas beaucoup de trafic.
Cette configuration ne vous permettra plus d'utiliser localhost. Vous aurez besoin d'un domaine qui autorise les sous-domaines à cause du paramètre : tld_length: 2
.
Vous pouvez, par exemple, utiliser dev.lvh.me:3000.
Pour l'activer, vous devez faire pointer *.lvh.me
sur votre IP locale, en modifiant le fichier de configuration des hôtes de votre ordinateur. Sur Mac ou Linux faites :
sudo nano /etc/hosts
Entrez votre mot de passe puis ajouter cette ligne à la fin du fichier :
127.0.0.1 subdomain.localhost
Votre fichier devrait maintenant ressembler à ça :
Vous devriez maintenant être en mesure d'utiliser dev.lvh.me:3000 pour lancer votre serveur local.
Configurer les règles Cross-Origin Resource Sharing (CORS)
Nous devons également configurer notre politique de partage de ressources provenant de plusieurs origines (CORS) pour permettre à notre site web statique de récupérer des données depuis notre application Rails.
Pour cela nous allons d'abord avoir besoin d'ajouter la gem rack-cors
à notre application :
gem "rack-cors", "~> 1.1", ">= 1.1.1"
N'oubliez pas de lancer un bundle install
pour installer la gem.
Maintenant, créons notre initializer cors.rb
:
Cette configuration permet aux navigateurs dont l'origine est www.yourmarketingwebsitedomain.com
de télécharger du contenu depuis le path /users/me
de notre plateforme.
Notez que le paramètre `origins` accepte une liste de valeurs, si vous souhaitez autoriser le téléchargement depuis plusieurs sources. Dans ce cas vous pouvez l’écrire de cette manière :
Etant donné que nous partageons les credentials, il n’est pas possible d’autoriser n’importe quelle origine avec un wildcard (origins(“*”)
).
L'option credentials: true
permet d'ajouter une en-tête de requête Access-Control-Allow-Credentials
, qui autorise le serveur à partager des informations d'identification avec le front-end lorsque les autres conditions sont remplies. Les informations d'identification sont les cookies, des en-têtes d'autorisation ou encore des certificats clients TLS.
Corriger l'erreur invalid host
Lorsque vous passez à un cookie cross-subdomain
, vous risquez de rencontrer une erreur étrange lors de l'exécution de vos tests de requête :
ArgumentError: invalid domain: ".example.com"
Cette erreur est causée par CGI, une dépendance de rack. Ce bug est corrigé dans sa version 0.3.6. Ainsi, pour le résoudre, il suffit de forcer sa version dans votre Gemfile
comme ceci :
gem "cgi", ">= 0.3.6"
N'oubliez pas d'exécuter ensuite un bundle install
dans votre terminal et de redémarrer votre serveur.
Ajouter un script JS pour récupérer la donnée
Maintenant que notre application Rails est configurée, nous pouvons passer à la partie front-end. Il suffit de coller ce code dans la section code du pied de page de votre page HTML :
Que fait ce code ?
Il ajoute une balise de script qui va effectuer une requête
fetch()
sur leENDPOINT
. NB : Assurez-vous de remplacer la valeur de la constante par le domaine et le chemin de votre application Rails.Si l'utilisateur n'est pas connecté à l'application Rails, elle s'arrêtera là à cause de la configuration
redirection : "error"
(Le comportement classique de Devise est de rediriger vers la page de connexion).Si l'utilisateur est connecté à l'application Rails, il déclenchera une fonction
fillUserData()
. Cette fonction prend tous les éléments DOM avec la classe.remotefill
et, sur la base de leur attributremote-key
, elle trouve les données dans la réponse renvoyée et remplace le contenu de la page avec la valeur correspondante.
Considérons l'exemple suivant.
La réponse de votre application Rails est :
et votre site front-end contient les éléments suivants :
une fois exécutée, la fonction fillUserData()
remplacera ces éléments par :
Gardez cependant en tête qu'il s'agit de code exécuté uniquement en front-end, ainsi si vous décidez de masquer ou d'afficher du contenu de votre site côté client, n'importe quel internaute avec quelques connaissances de développement web pourra y accéder.
Bonus : Utiliser le stockage local pour la mise en cache des données
Dans son état actuel, cette implémentation déclencherait une requête HTTP vers votre application Rails à chaque chargement de page. Ce n'est pas optimal. Nous pourrions utiliser le localStorage du navigateur pour mettre en cache ces données côté client et réduire le nombre de requêtes HTTP.
Pour cela, on peut modifier le script comme suit :
Le script vérifie si le localStorage du navigateur contient les userData
récupérés il y a moins de 5 minutes :
1. Si oui, il déclenchera notre fillUserData()
avec son contenu ;
2. Autrement, il effectuera la requête réseau pour récupérer les données de notre application Rails et déclenchera ensuite la fonction fillUserData()
.
Modifions notre script comme suit :
Notez que localStorage n'implémente pas de délai d'expiration de manière intrinsèque. C'est pourquoi nous avons créé les fonctions getWithExpiry
et setWithExpiry
. Pour plus d'informations, consultez l'article de Soham Kamani.
Le job de tes rêves !
Comme vous le savez, derrière Ruby Biscuit, il y a Capsens 👋 , nous sommes une agence web spécialisée dans le Ruby on Rails depuis 10 ans.
Avec le temps on s'est rendu compte que beaucoup de dévs choisissent leur entreprise un peu par hasard alors qu'ils pourraient mieux s'épanouir et se valoriser dans des structures qui leur correspondent bien.
Ce qui tombe super bien c'est que chez Capsens nous avons une excellente connaissance de l'écosystème Ruby on Rails en France, avec un réseau d'entreprises considérable.
C'est pourquoi on vous annonce à travers cette newsletter que nous mettons à profit notre connaissance du métier pour vous aider à trouver le poste de vos rêves !
Concrètement :
Tu souhaites trouver le job de tes rêves ? Alors réponds à cet e-mail ! Tu peux dire "Coucou", ça suffit !
On te proposera aussitôt des créneaux pour une visio afin de faire connaissance
Puis nous te proposerons 3 entreprises qui correspondent à qui tu es. Et pour chacun de ces postes :
Tu pourras discuter avec un développeur de l'équipe (pas le recruteur lui-même) afin de savoir comment ça se passe de l'intérieur. No bullshit
Un développeur de Capsens t'aidera à :
Analyser le poste : ce qui a l'air bien, les dangers, quelles conditions poser pour que tout se passe bien
Te préparer pour que tu décroches le bon poste
Tu veux aller vers une vie professionnelle plus épanouie ? 😀 Eh bien, on attend ton e-mail ! Et si tu aimes déjà ton travail, ne nous contacte surtout pas ! Ou alors fais-le pour nous recommander ta boite 😉
Le Défi
Le défi de la dernière newsletter n'était pas des moindres !
Pour rappel, la consigne était la suivante :
Calculer le triangle de Pascal jusqu'à un nombre donné de lignes.
Dans le triangle de Pascal, chaque nombre est calculé en additionnant les nombres à droite et à gauche de la position actuelle dans la ligne précédente.
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
# ... etc
Voici la réponse proposée par Antoine :
def pascal(i)
result = []
i.times do
previous = 0
result.map! do |element|
previous + (previous = element)
end
result << 1
end
result
end
Les règles du prochain défi :
Écris une méthode qui pourra déterminer si une année donnée est bissextile ou non. Au cas où vous ne connaîtriez pas les règles, les voici :
Les années divisibles par 4 sont bissextiles
Les années divisibles par 100 ne sont pas bissextiles
Les années divisibles par 400 sont bissextiles.
Tout comme les dernières fois, envoyez-moi vos réponses en répondant à cette newsletter et je publierai la meilleure dans la prochaine édition !
Si tu es arrivé jusqu'ici tu as deux options :
Soit tu n'es pas encore abonné et c'est alors le moment
Soit tu l'es déjà et dans ce cas je compte sur toi pour partager cette édition 😉
Ismaël, Antoine et Mélanie et les nombreux relecteurs 🙂