React.js - Indicateur de "force" d'un mot de passe
Tags: react, javascript, security, tutorial
Published: 2022-02-04 19:12:24

Pour sécuriser les mots de passe, on demande souvent à l’utilisateur d’utiliser un mot de passe “complexe” en ajoutant par exemple une majuscule, un caractère spécial ou un chiffre. Cependant, ces méthodes ne permettent pas de créer un mot de passe aussi sécurisé qu’on le pense.

Par exemple, le mot de passe: `[Password1234]` réponds à toutes ces règles, cependant il est l’un des plus testés lors des attaques. 

## Comment faire alors ?

L’une des solutions pour créer un processus de validation de mot de passe fiable est d’utiliser une librairie comme `zxcvbn-ts` ([https://zxcvbn-ts.github.io/zxcvbn/](https://zxcvbn-ts.github.io/zxcvbn/)). Cette librairie permet d’estimer la complexité d’un mot de passe et ainsi d’empêcher l’utilisateur d’utiliser un mot de passe jugé trop faible.

 

## Implémentation en React.js

L’idée est de calculer le score du mot de passe indiqué par l’utilisateur et en fonction de ce score affiché les indicateurs qui correspondent :


![Indicateur de mot de passe](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/38mw1c0o7ffvm2nrihjt.gif)

`App.tsx` :

```tsx
import React, { useEffect, useState } from "react";
import { zxcvbn } from "@zxcvbn-ts/core";

import { Indicators } from "./components/Indicators";

import "./App.css";

interface Indicator {
  score: number;
}

const App = () => {
  const [password, setPassword] = useState("");
  const [indicator, setIndicator] = useState<Indicator>({ score: -1 });

  useEffect(() => {
    if (password === "") return;

    setIndicator(zxcvbn(password));
  }, [password]);

  const { score } = indicator;

  return (
    <div className="d-block mx-4">
      <div className="position-relative mt-3">
        <label htmlFor="password-input" className="mr-2">
          Mot de passe
        </label>
        <input
          is="password-input"
          type="password"
          onChange={(event) => setPassword(event.target.value)}
          value={password}
          placeholder={"**********"}
        />
        {password !== "" && <Indicators score={score} />}
      </div>
    </div>
  );
};

export default App;
```

`Indicators.tsx`

```tsx
import React from "react";

const colors = {
  0: "#e5e5e5",
  1: "#9B2C2C",
  2: "#D44949",
  3: "#DCA02D",
  4: "#387F95",
  5: "#48AE65"
};

const getColor = (power, index) => {
  if (power > index) {
    return colors[power];
  }
  return colors[0];
};

const indicatorIndexes = [0, 1, 2, 3, 4];

const Indicators = ({ score }: { score: number }) => (
  <div className="mt-2 indicator-container">
    {indicatorIndexes.map((indicatorIndex, index) => (
      <div
        className="indicator"
        key={indicatorIndex}
        style={{ backgroundColor: getColor(score + 1, indicatorIndex) }}
      />
    ))}
  </div>
);

export { Indicators };
```

`App.css`

```css
.indicator {
  height: 4px;
  border-radius: 4px;
  width: 15%;
  margin-right: 8px;
}

.indicator-container {
  flex-direction: row;
  display: flex;
}
```

## Pour allez plus loin

A présent, on va ajouter des options dans notre validation. Avec pour objectif pour rendre notre validation de mot de passes plus sécurisé. On va aussi ajouter des suggestions pour indiquer à l’utilisateur comment rendre son mot de passe plus fort.


![Indicateur de mot de passe avec suggestions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x3ws0pktlda91mi0v3xt.gif)

`App.tsx` :

```tsx
import React, { useEffect, useState } from "react";
import { zxcvbn, ZxcvbnOptions } from "@zxcvbn-ts/core";
import zxcvbnCommonPackage from "@zxcvbn-ts/language-common";
import zxcvbnFrPackage from "@zxcvbn-ts/language-fr";
import { FeedbackType } from "@zxcvbn-ts/core/dist/types";

import { Indicators } from "./components/Indicators";
import { Suggestions } from "./components/Suggestions";

import "./App.css";

const options = {
  translations: zxcvbnFrPackage.translations,
  graphs: zxcvbnCommonPackage.adjacencyGraphs,
  dictionary: {
    ...zxcvbnCommonPackage.dictionary,
    ...zxcvbnFrPackage.dictionary
  }
};

ZxcvbnOptions.setOptions(options);

interface Indicator {
  score: number;
  feedback: FeedbackType;
}

const App = () => {
  const [password, setPassword] = useState("");
  const [indicator, setIndicator] = useState<Indicator | null>();

  useEffect(() => {
    if (password === "") return;

    setIndicator(zxcvbn(password));
  }, [password]);

  const score = indicator ? indicator.score : -1;
  const feedback = indicator ? indicator.feedback : undefined;

  return (
    <div className="d-block mx-4">
      <div className="position-relative mt-3">
        <label htmlFor="password-input" className="mr-2">
          Mot de passe
        </label>
        <input
          is="password-input"
          type="password"
          onChange={(event) => setPassword(event.target.value)}
          value={password}
          placeholder={"**********"}
        />
        {password !== "" && <Indicators score={score} />}
        {feedback && feedback.warning.length > 0 && (
          <Suggestions suggestions={feedback.suggestions} />
        )}
      </div>
    </div>
  );
};

export default App;
```

`Suggestions.tsx`

```tsx
import React from "react";

const Suggestions = ({ suggestions }: { suggestions: string[] }) => (
  <ul>
    {suggestions.map((suggestion, index) => (
      <li key={suggestion}>{suggestion}</li>
    ))}
  </ul>
);

export { Suggestions };
```

### Lien codesandbox:

[https://codesandbox.io/s/password-zxcvbn-react-ts-3lt0q](https://codesandbox.io/s/password-zxcvbn-react-ts-3lt0q?file=/public/manifest.json)