
Partager:
Oluwatobi is a software developer and writer. He loves to simplify complex topics, making them easy for anyone to understand
Mise en œuvre de l'authentification multifactorielle dans Go with Verify
Temps de lecture : 12 minutes
Que vous travailliez sur un projet comptant des millions d'utilisateurs ou sur un petit projet annexe, il est primordial de s'assurer que les applications que vous créez sont sûres et sécurisées. Les applications qui ne sont pas sécurisées pourraient exposer les données des utilisateurs à des pirates informatiques, ce qui entraînerait une perte d'argent et de confiance. C'est le travail d'un développeur de logiciels de s'assurer que le code écrit adopte une approche axée sur la sécurité, en tenant compte de tous les détails. L'un des moyens de garantir la sécurité de vos Applications est d'intégrer un système d'authentification multifactorielle, communément appelé MFA.
L'authentification multifactorielle est utilisée pour ajouter une sécurité supplémentaire aux applications ainsi que pour vérifier l'identité d'un utilisateur. Dans cet article, nous allons apprendre à mettre en œuvre un système d'authentification multifactorielle à l'aide du langage de programmation Go et de l Verify API de Vonage.
Conditions préalables
Pour suivre cet article, vous aurez besoin de :
Go (version 1.14 ou supérieure)
Modules Go activés
Un éditeur de texte de votre choix
Une connaissance de base de Go
Compte API Vonage
Pour compléter ce tutoriel, vous aurez besoin d'un Account API de Vonage. Si vous n'en avez pas encore, vous pouvez vous inscrire dès aujourd'hui et commencer à construire avec du crédit gratuit. Une fois que vous avez un Account, vous pouvez trouver votre clé API et votre secret API en haut du tableau de bord de l'API de Vonage.
Configuration du projet
Nous allons créer une application simple qui génère un code de vérification et l'envoie à un numéro de téléphone fourni par l'utilisateur. L'utilisateur saisit le code sur une page de confirmation et l'application lui confirme qu'il est valide.
App workflow
L'image ci-dessus est une esquisse du flux de travail de l'application que nous allons construire dans cet article.
Pour commencer, créez les fichiers et les dossiers correspondant à l'arborescence ci-dessous :
├── static/ │ ├── index.html │ ├── form.html ├── .env ├── utils/ │ ├── verify.go └── server.go
Le dossier statique contient deux pages HTML (formulaires) qui seront utilisées pour recueillir le numéro de téléphone et le code de confirmation de l'utilisateur, respectivement.
Les fichiers Env sont utilisés pour stocker des variables d'environnement et des valeurs qui doivent rester privées. Le fichier .env contiendra la clé et le secret de l'API de Vonage que vous pouvez obtenir à partir du tableau de bord.
Le répertoire utils contient un fichier verify.go qui contient tout le code directement lié à l'API Verify de Vonage. Le fichier server.go à la racine de notre projet contiendra la logique liée au serveur qui sert les fichiers HTML.
Configurer les vues
La première étape de la création de notre application web consiste à créer un simple serveur web qui servira les formulaires HTML. Dans le fichier server.go inclure les éléments suivants :
func main() {
//Create the fileServer Handler
fileServer := http.FileServer(http.Dir("./static"))
//Create a New Serve Mux to register handler
mux := http.NewServeMux()
mux.Handle("/", fileServer)
//Create the server on Port 8080 and print start message!
fmt.Printf("Starting server at port 8080\n")
log.Fatal(http.ListenAndServe(":8080", mux))
}Nous importons le net/http package et invoquons sa fonction FileServer en lui passant comme premier argument la route vers le répertoire static comme premier argument. La fonction http.FileServer renvoie un gestionnaire que nous enregistrons sur un modèle d'URL. Pour enregistrer un gestionnaire, nous devons créer un nouveau mux serve à l'aide de la fonction http.NewServeMux et invoquer sa méthode Handle en passant par / comme motif avec le gestionnaire de serveur de fichiers que nous avons généré ci-dessus. Ensuite, nous créons le serveur en utilisant la méthode http.ListenAndServe à l'aide de la méthode Nous passons le port et le mux serve que nous avons créé plus haut, puis nous imprimons un message dans le terminal pour nous informer que le serveur est opérationnel.
Lorsque vous exécutez le fichier server.go vous obtiendrez un message de démarrage indiquant que le serveur a démarré. Cependant, si vous visitez localhost:8080/ dans le navigateur, vous ne verrez qu'une page blanche. C'est parce que notre fichier index.html est vide.
Dans le index.html filecréez un formulaire intitulé S'inscrire avec un champ de saisie pour le numéro de téléphone de l'utilisateur et un bouton d'envoi :
<form method="POST" action="/form">
<h2>Register</h2>
<label>Please Input your phone number:</label><br/>
<input name="phone" type="tel" required value="" >
<input type="submit" value="Submit">
</form>
La méthode du formulaire est POST puisque le serveur recevra les données fournies par l'utilisateur. Nous définissons l'action du formulaire comme une route, /formque nous mettrons à disposition pour traiter les données renvoyées par le formulaire d'inscription.
Maintenant que la première page d'enregistrement est prête, nous pouvons créer une deuxième page pour la confirmation du code envoyé à l'utilisateur. La page de confirmation est similaire à la page d'enregistrement, avec seulement quelques changements dans les libellés des champs et des entrées :
<form method="POST" action="/confirm">
<h2>Confirm Phone Number</h2>
<label for="phonenum">Please Input the confirmation code sent to your phone:</label><br/>
<input id="phonenum" name="confirmation" type="tel" required value="" >
<input name="phone" type="hidden" value="{{ .Phone }}">
<input name="requestId" type="hidden" value="{{ .Id }}">
<input type="submit" value="Submit">
</form>
Le texte complet des deux fichiers HTML peut être consulté dans le fichier exemple. Après avoir configuré les deux fichiers HTML, redémarrez le serveur. Si tout s'est bien passé, le fichier index.html devrait apparaître lorsque vous visitez localhost:8080 dans le navigateur.
Registration form
Si vous entrez un numéro de téléphone et que vous cliquez sur le bouton d'envoi, vous remarquerez qu'il est redirigé vers localhost:8080/form comme spécifié précédemment à l'aide de l'attribut d'action HTML. La route /form n'est pas actuellement configurée pour servir le fichier form.html pour le servir. Dans la section suivante, nous allons créer un gestionnaire pour la route /form qui acceptera le numéro de téléphone comme argument et rendra le fichier confirm.html fichier.
Mise en œuvre de l'authentification multifactorielle
Dans cette section, nous aborderons le cœur de notre application - l'authentification multifactorielle - et nous le coderons. Nous utiliserons l'API Verify de Vonage pour mettre en œuvre l'authentification multifactorielle.
La mise en œuvre de l'AMF avec l'API Verify est un processus à double sens :
La première étape consiste à lancer une demande de vérification. À ce stade, un code est envoyé au téléphone de l'utilisateur.
La deuxième étape consiste à vérifier si le code de vérification fourni par l'utilisateur est correct.
Lorsqu'une demande de Verify est lancée, un numéro de téléphone de Request_id sera automatiquement généré à partir du numéro de téléphone de l'utilisateur. Cet identifiant sera utilisé lors de la vérification à l'étape 2.
Les développeurs de Vonage ont créé un paquetage paquet Go pour interagir avec une multitude d'API de Vonage, y compris Verify. Pour commencer, installez le paquetage dans votre projet en lançant go get github.com/vonage/vonage-go-sdk dans le terminal.
Maintenant que tout est prêt, plongeons dans le vif du sujet !
Dans le fichier verify.go créer quatre fonctions :
1. Créer un client
Cette fonction contient la logique de création d'un client. Un client est nécessaire pour interagir avec l'API Verify. Pour créer un client, nous invoquons la fonction NewVerifyClient fournie par le paquetage vonage-go que nous avons importé précédemment. La fonction NewVerifyClient nécessite un ensemble d'authentification, que nous créons en invoquant la fonction CreateAuthFromKeySecret en lui transmettant la clé et le secret de l'API
Il est conseillé de stocker les informations sensibles sous forme de variables d'environnement afin d'éviter qu'elles ne tombent entre de mauvaises mains. Dans le fichier .env créé ci-dessus, ajoutez votre clé d'API et votre secret au format API_KEY=0000000.
A ce stade, la fonction createClient ressemble à ceci :
func createClient() *vonage.VerifyClient{
Key, _ := os.LookupEnv("API_KEY")
Secret, _ := os.LookupEnv("API_SECRET")
auth := vonage.CreateAuthFromKeySecret(Key, Secret)
client := vonage.NewVerifyClient(auth)
return client
}2. fonction d'initialisation
La fonction init est prédéfinie par Go, et elle est utilisée pour initialiser notre application. Nous l'utiliserons pour charger nos variables d'environnement à partir du fichier .env avant que le reste de notre code ne soit exécuté, mais nous devons d'abord installer un paquetage couramment utilisé pour charger les variables d'environnement. Dans le terminal, lancez go get github.com/joho/godotenv' to install the package. Next, add the corresponding import. Theinitfunction will contain just a few lines of code which invokes thegodotenv` Fonction de chargement.
func init() {
// loads values from .env into the system
if err := godotenv.Load(); err != nil {
log.Print("No .env file found")
}
}3. Fonction VerStart
C'est dans cette fonction que nous commençons la demande de vérification. La fonction va être exportée vers le paquetage principal, nous mettons donc une majuscule à la première lettre de la fonction, comme c'est le cas en Go. Dans la fonction VerStart nous démarrons la demande en invoquant la méthode de Vonage SDK Request du SDK Vonage sur un client. Nous invoquons la fonction createClient que nous avons créée ci-dessus pour obtenir un client sur lequel nous pouvons appeler la méthode Request méthode :
func VerStart(phoneNumber string) string{
client := createClient()
verification, _, err := client.Request(phoneNumber, "Go-Tut MFA", vonage.VerifyOpts{
CodeLength: 6,
})
if err != nil {
log.Fatal(err)
}
return verification.RequestId
}La méthode Request prend en compte trois paramètres :
Le numéro de téléphone à vérifier
La marque
Une structure d'options utilisée pour personnaliser l'OTP qui sera envoyé à l'utilisateur.
Dans notre application, nous voulons que le numéro de téléphone soit dynamique, nous le spécifions donc en tant que paramètre de la fonction Request qui sera transmis lorsque la fonction sera invoquée. Le nom de la marque est une chaîne courte qui indique la marque qui envoie le SMS - pour cet exemple, j'ai utilisé "Go-tut MFA". Lorsque les paramètres requis sont passés dans la méthode Request nous pouvons nous occuper de ses valeurs de retour. La méthode Request renvoie une réponse de vérification contenant un champ d'état, une réponse http que nous ignorerons et un type d'erreur. Nous pouvons renvoyer le statut de la réponse de vérification et traiter l'erreur.
4. Fonction verCheck
Cette fonction héberge la logique liée à la confirmation du code de vérification que l'utilisateur soumet. Cette fonction est similaire à la fonction verStart vue ci-dessus :
func VerCheck(reqId, code string) string{
client := createClient()
response, _, err := client.Check(reqId, code)
if err != nil {
log.Fatal(err)
}
if err != nil {
log.Fatal(err)
}
return response.Status
}La fonction verCheck prend en compte l'identifiant de la demande, qui est un argument obligatoire pour la fonction Check et un paramètre code qui doit être le code de confirmation envoyé à l'utilisateur. Cette fonction renvoie un statut, qui est de type chaîne de caractères. Le statut renvoyé indique si l'utilisateur a saisi le bon code de confirmation. Le statut de la réponse sera 0 si et seulement si le code de confirmation saisi par l'utilisateur est correct.
Dans la section suivante, nous verrons où les quatre fonctions créées ci-dessus doivent être invoquées pour que notre application fonctionne.
Assembler l'application
Maintenant que toutes les fonctions utilitaires dont notre application a besoin sont prêtes, nous pouvons rassembler le tout dans une application fonctionnelle. Nous avons déjà rendu notre page d'index, mais nous devons également rendre la page de confirmation qui s'affichera une fois que l'on aura cliqué sur le bouton de soumission. En Go, cette tâche est assez différente de celle des autres langages. Nous devons créer une fonction de traitement et l'attacher à la route /form qui reçoit les données du formulaire d'inscription. Ensuite, nous allons créer une fonction appelée formHandler pour mettre en œuvre l'interface de gestion de Go. Nous analysons ensuite le corps de la requête afin d'accéder aux données du formulaire à partir de l'élément index.html. Nous utilisons la méthode ParseForm de la requête http.
Les données du formulaire peuvent être extraites du formulaire analysé à l'aide de la méthode formValue méthode. Cette méthode prend en compte le nom spécifié dans l'attribut HTML input de notre fichier index.html de notre fichier. Nous pouvons alors importer et invoquer la fonction verRequest avec le numéro de téléphone.
Pour afficher la page de confirmation lorsqu'un utilisateur saisit son numéro de téléphone, nous devons créer une fonction distincte. Bien que nous puissions simplement utiliser la fonction http.Servefile pour afficher notre fichier form.html mais ce n'est pas la solution idéale dans notre cas. Outre le rendu du fichier de confirmation, nous devons également transmettre le numéro de téléphone de l'utilisateur et l'identifiant de la demande afin qu'ils puissent être utilisés pour invoquer la fonction utilitaire verCheck ci-dessus.
La fonction de rendu est assez basique - elle reçoit une requête http, un rédacteur de réponse et une interface. La fonction render utilise le paquetage http/template pour analyser les fichiers et les exécuter avec les données fournies (le numéro de téléphone de l'utilisateur et l'identifiant de la demande).
func render(w http.ResponseWriter, filename string, data interface{}) {
//parse the provided file
tmpl, err := template.ParseFiles(filename)
if err != nil {
log.Println(err)
}
//execute the file
if err := tmpl.Execute(w, data); err != nil {
log.Println(err)
}
}La fonction de rendu étant prête, nous pouvons l'invoquer dans la fonction formHandler en passant la route vers le fichier de formulaire et notre message. Pour passer un message, nous créons simplement une structure qui définit les champs que nous souhaitons passer au fichier form.html :
type Message struct {
Phone string
Id string
}Nous devons créer des champs cachés dans le fichier form.html pour recevoir les variables dans la structure du message en ajoutant ce qui suit au formulaire :
<input name="phone" type="hidden" value="{{ .Phone }}">
<input name="requestId" type="hidden" value="{{ .Id }}">À ce stade, l'ensemble de la fonction formHandler ressemble à ceci
func formHandler (w http.ResponseWriter, r *http.Request){
//Parse the form
if err := r.ParseForm(); err != nil {
fmt.Fprintf(w, "ParseForm() err: %v", err)
return
}
//Get the value of the Input from the form
Phone := r.FormValue("phone")
Id := verify.VerStart(Phone)
msg := &Message{
Id: Id,
Phone: Phone,
}
//Render the form.
render(w, "./static/form.html", msg)
}
Une dernière chose à faire avant de tester les choses est d'enregistrer la fonction formHandler à la route /form à la route. Pour ce faire, nous n'ajoutons qu'une seule ligne de code :
mux.HandleFunc("/form", formHandler)Et le tour est joué ! Vous pouvez exécuter le programme, saisir votre numéro de téléphone sur la page d'enregistrement et cliquer sur soumettre. Vous devriez voir qu'en cliquant sur le bouton d'envoi, vous êtes dirigé vers la page de confirmation et que vous recevrez un SMS sur votre téléphone portable.
SMS code
Notre application se met bien en place. La prochaine chose à faire est de gérer la saisie du code de confirmation par l'utilisateur. Dans l'état actuel de notre application, si un utilisateur saisit son code de confirmation et clique sur soumettre, il est redirigé vers la route de confirmation qui n'est pas encore configurée.
Nous allons configurer le gestionnaire de confirmation de manière à ce qu'il reçoive le code de confirmation du client et qu'il invoque la fonction verCheck util. Le gestionnaire de confirmation est assez similaire au gestionnaire de formulaire, à quelques différences près.
Pour commencer, nous créons la fonction confirmHandler pour implémenter l'interface du gestionnaire et analyser la requête entrante, comme nous l'avons fait pour la fonction formHandler function. Ensuite, nous extrayons du formulaire les valeurs nécessaires à l'exécution de la fonction verCheck à partir du formulaire. Nous devons extraire trois valeurs :
Le numéro de téléphone
L'identifiant de la demande
Le code de confirmation
Les deux premières valeurs ont été transmises par la fonction formHandler à l'aide de la fonction render et ont été cachées dans l'élément form.html file. Pour obtenir les valeurs, il suffit d'ajouter les lignes suivantes, dont nous avons parlé précédemment lors de la création de la fonction formHandler :
Id := r.FormValue("requestId")
phone := r.FormValue("phone")
confirmation := r.FormValue("confirmation")Maintenant que nous avons reçu toutes les valeurs nécessaires à la deuxième étape de notre flux de travail (vérification du code), nous pouvons invoquer la commande verCheck functionen indiquant l'identifiant de la demande et le code de confirmation que nous venons d'extraire. Vous vous souvenez que nous avons configuré la fonction verCheck pour qu'elle renvoie un état de réussite ? Nous pouvons vérifier cet état pour voir si le code de confirmation saisi par l'utilisateur est correct et imprimer un message de réussite ou d'échec sur une nouvelle page web à l'aide de la fonction fmt.FPrint pour imprimer un message de réussite ou d'échec sur une nouvelle page web. À ce stade, la page confirmHandler devrait ressembler à ceci :
func confirmHandler(w http.ResponseWriter, r *http.Request) {
//Parse the form
if err := r.ParseForm(); err != nil {
fmt.Fprintf(w, "ParseForm() err: %v", err)
return
}
//Extract the Form values
Id := r.FormValue("requestId")
phone := r.FormValue("phone")
//Receive the confirmation code
confirmation := r.FormValue("confirmation")
//Verify the Confirmation code
response := verify.VerCheck(Id, confirmation)
//Check if the confirmation code is incorrect
if response != "0" {
fmt.Fprint(w,"Verification failed! Input the correct code sent to ", phone)
return
}
fmt.Fprint(w,"🎉 Success! 🎉")
}À ce stade, nous avons presque terminé notre application. La dernière chose à faire est d'enregistrer la route confirmHandler en ajoutant la ligne de code suivante à la fonction principale :
mux.HandleFunc("/confirm", confirmHandler)Et c'est parti ! Si vous démarrez votre serveur et visitez l'adresse sur le navigateur, vous devriez voir le formulaire d'enregistrement. Si vous introduisez votre numéro de téléphone et cliquez sur soumettre, vous recevrez le code sur votre téléphone portable. Vous serez ensuite automatiquement redirigé vers la page de confirmation où vous pourrez introduire le code. Lorsque vous soumettez le code, vous devriez voir le succès s'imprimer sur la page.
Conclusion
Jusqu'à présent, nous avons créé une application en Go qui utilise l'API Verify de Vonage et nous nous sommes familiarisés avec le développement web avec Go. J'espère que cela vous a plu. Le code complet de cette application est disponible sur GitHub.