React Autenticación y control de acceso | Programar Plus

La autenticación y el control de acceso son necesarios para la mayoría de las aplicaciones, pero a menudo nos distraen de la creación de funciones básicas. En este artículo, cubriré una forma sencilla de agregar autenticación y control de acceso en React.

En lugar de agregar una biblioteca estática que debe mantener actualizada o volver a investigar cada vez que crea un proyecto, usaremos un servicio que se mantiene actualizado automáticamente y es una alternativa mucho más simple a Auth0, Okta y otros. .

Reaccionar autenticación

Hay dos cosas principales que debe hacer su aplicación React para iniciar sesión en un usuario:

  1. Obtenga un token de acceso de un servidor de autenticación
  2. Envíe el token de acceso a su servidor backend con cada solicitud posterior

Estos pasos son los mismos para casi todas las autenticaciones, ya sea correo electrónico y contraseña estándar, enlaces mágicos o proveedores de inicio de sesión único (SSO) como Google, Azure o Facebook.

En última instancia, queremos que nuestra aplicación React envíe una solicitud inicial a un servidor de autenticación y que ese servidor genere un token de acceso que podamos usar.

Tokens de acceso JWT

Hay diferentes opciones para qué tipo de token de acceso usar, y los tokens web JSON (JWT) son una excelente opción. Los JWT son tokens compactos y seguros para URL que su aplicación React puede usar para autenticación y control de acceso.

Cada JWT tiene un objeto JSON como su “carga útil” y está firmado de manera que su servidor backend pueda verificar que la carga útil sea auténtica. Un ejemplo de JWT se ve así:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImF1dGhvcml6YXRpb24iOiJhZG1pbiJ9.f7iKN-xi24qrQ5NQtOe0jiriotT-rve3ru6sskbQXnA

La carga útil de este JWT es la sección central (separada por puntos):

eyJ1c2VySWQiOjEsImF1dGhvcml6YXRpb24iOiJhZG1pbiJ9

La carga útil de JWT se puede decodificar desde base64 para producir el objeto JSON:

JSON.parse(atob("eyJ1c2VySWQiOjEsImF1dGhvcml6YXRpb24iOiJhZG1pbiJ9"));

// =>
{
  “userId”: 1,
  “authorization”: “admin”
}

Es importante tener en cuenta que cualquier persona con JWT puede leer esta carga útil, incluida su aplicación React o un tercero. Cualquiera que tenga el JWT puede leer su contenido.

Sin embargo, solo el servidor de autenticación puede generar JWT válidos. Su aplicación React, su servidor backend o un tercero malintencionado no pueden generar JWT válidos, solo leerlos y verificarlos.

Cuando su servidor backend recibe una solicitud con un JWT, debe verificar que el JWT sea auténtico al compararlo con la clave pública para ese JWT. Esto permite que su servidor de aplicaciones verifique los JWT entrantes y rechace cualquier token que no haya sido creado por el servidor de autenticación (o que haya expirado).

El flujo para usar un JWT en su aplicación React se ve así:

  1. Su aplicación React solicita un JWT del servidor de autenticación cada vez que el usuario desea iniciar sesión.
  2. El servidor de autenticación genera un JWT usando una clave privada y luego envía el JWT a su aplicación React.
  3. Su aplicación React almacena este JWT y lo envía a su servidor backend cada vez que su usuario necesita realizar una solicitud.
  4. Su servidor backend verifica el JWT usando una clave pública y luego lee la carga útil para determinar qué usuario está realizando la solicitud.

Cada uno de estos pasos es fácil de escribir, pero cada paso tiene sus propios inconvenientes cuando realmente desea implementarlo y mantenerlo seguro. Especialmente con el tiempo, a medida que surgen nuevos vectores de amenazas y se necesitan parches o soporte de nuevas plataformas, la sobrecarga de seguridad puede acumularse rápidamente.

Userfront elimina la complejidad de autenticación en las aplicaciones React

Userfront es un marco que abstrae la complejidad de la autenticación. Esto hace que sea mucho más fácil para usted trabajar con la autenticación en una aplicación React y, quizás lo más importante, mantiene todos los protocolos de autenticación actualizados automáticamente con el tiempo.

La filosofía subyacente de Userfront es que la autenticación de clase mundial no debe requerir esfuerzo; debe ser fácil de configurar y las actualizaciones de seguridad deben realizarse automáticamente. Userfront tiene todas las comodidades de autenticación, inicio de sesión único (SSO), control de acceso y tenencia múltiple, con un nivel gratuito listo para producción de hasta 10,000 usuarios activos mensuales.

Para la mayoría de las aplicaciones de React modernas, es una gran solución.

Configurando la autenticación en React

Ahora veremos la construcción de todos los aspectos principales de la autenticación en una aplicación React. El código final para este ejemplo está disponible aquí.

Configure su aplicación React y ponga su canal de compilación en orden como prefiera. En este tutorial, usaremos Create React App, que hace gran parte del trabajo de configuración por nosotros, y también agregaremos React Router para el enrutamiento del lado del cliente. Comience instalando la aplicación Create React y React Router:

npx create-react-app my-app
cd my-app
npm install react-router-dom --save
npm start

Ahora nuestra aplicación React está disponible en http://localhost:3000.

Como dice la página, ahora podemos editar el src/App.js archivo para empezar a trabajar.

Reemplazar el contenido de src/App.js con lo siguiente, basado en el inicio rápido de React Router:

// src/App.js

import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";

export default function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="https://css-tricks.com/">Home</Link>
            </li>
            <li>
              <Link to="/login">Login</Link>
            </li>
            <li>
              <Link to="/reset">Reset</Link>
            </li>
            <li>
              <Link to="/dashboard">Dashboard</Link>
            </li>
          </ul>
        </nav>

        <Switch>
          <Route path="/login">
            <Login />
          </Route>
          <Route path="/reset">
            <PasswordReset />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
          <Route path="https://css-tricks.com/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  return <h2>Home</h2>;
}

function Login() {
  return <h2>Login</h2>;
}

function PasswordReset() {
  return <h2>Password Reset</h2>;
}

function Dashboard() {
  return <h2>Dashboard</h2>;
}

Ahora tenemos una aplicación muy sencilla con enrutamiento:

Ruta Descripción
/ Pagina de inicio
/login Página de inicio de sesión
/reset Página de restablecimiento de contraseña
/dashboard Panel de usuario, solo para usuarios registrados

Esta es toda la estructura que necesitamos para comenzar a agregar autenticación.

Registro, inicio de sesión y restablecimiento de contraseña con Userfront

Continúe y cree una cuenta de Userfront. Esto le proporcionará un formulario de registro, un formulario de inicio de sesión y un formulario de restablecimiento de contraseña que puede utilizar para los siguientes pasos.

En la sección Kit de herramientas de su panel de Userfront, puede encontrar las instrucciones para instalar su formulario de registro:

Siga las instrucciones instalando el paquete Userfront React con:

npm install @userfront/react --save
npm start

Agregue el formulario de registro a su página de inicio importando e inicializando Userfront, y luego actualizando el Home() función para representar el formulario de registro.

// src/App.js

import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import Userfront from "@userfront/react";

Userfront.init("demo1234");

const SignupForm = Userfront.build({
  toolId: "nkmbbm",
});

export default function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="https://css-tricks.com/">Home</Link>
            </li>
            <li>
              <Link to="/login">Login</Link>
            </li>
            <li>
              <Link to="/reset">Reset</Link>
            </li>
            <li>
              <Link to="/dashboard">Dashboard</Link>
            </li>
          </ul>
        </nav>

        <Switch>
          <Route path="/login">
            <Login />
          </Route>
          <Route path="/reset">
            <PasswordReset />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
          <Route path="https://css-tricks.com/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  return (
    <div>
      <h2>Home</h2>
      <SignupForm />
    </div>
  );
}

function Login() {
  return <h2>Login</h2>;
}

function PasswordReset() {
  return <h2>Password Reset</h2>;
}

function Dashboard() {
  return <h2>Dashboard</h2>;
}

Ahora la página de inicio tiene su formulario de registro. Intente registrar un usuario:

Su formulario de registro está en “modo de prueba” de forma predeterminada, lo que creará registros de usuario en un entorno de prueba que puede ver por separado en su panel de Userfront:

Continúe agregando sus formularios de inicio de sesión y restablecimiento de contraseña de la misma manera que agregó su formulario de registro:

// src/App.js

import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import Userfront from "@userfront/react";

Userfront.init("demo1234");

const SignupForm = Userfront.build({
  toolId: "nkmbbm",
});
const LoginForm = Userfront.build({
  toolId: "alnkkd",
});
const PasswordResetForm = Userfront.build({
  toolId: "dkbmmo",
});

export default function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="https://css-tricks.com/">Home</Link>
            </li>
            <li>
              <Link to="/login">Login</Link>
            </li>
            <li>
              <Link to="/reset">Reset</Link>
            </li>
            <li>
              <Link to="/dashboard">Dashboard</Link>
            </li>
          </ul>
        </nav>

        <Switch>
          <Route path="/login">
            <Login />
          </Route>
          <Route path="/reset">
            <PasswordReset />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
          <Route path="https://css-tricks.com/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  return (
    <div>
      <h2>Home</h2>
      <SignupForm />
    </div>
  );
}

function Login() {
  return (
    <div>
      <h2>Login</h2>
      <LoginForm />
    </div>
  );
}

function PasswordReset() {
  return (
    <div>
      <h2>Password Reset</h2>
      <PasswordResetForm />
    </div>
  );
}

function Dashboard() {
  return <h2>Dashboard</h2>;
}

En este punto, su registro, inicio de sesión y restablecimiento de contraseña deberían ser funcionales.

Sus usuarios pueden registrarse, iniciar sesión y restablecer su contraseña.

Control de acceso en React

Por lo general, no queremos que los usuarios puedan ver el panel a menos que hayan iniciado sesión. Esto se conoce como ruta protegida.

Siempre que un usuario no haya iniciado sesión pero intente visitar /dashboard, podemos redirigirlos a la pantalla de inicio de sesión.

Podemos lograr esto actualizando el Dashboard componente en src/App.js para manejar la lógica condicional.

Cuando un usuario inicia sesión con Userfront, tendrá un token de acceso disponible como Userfront.accessToken(). Podemos verificar este token para determinar si el usuario ha iniciado sesión. Si el usuario ha iniciado sesión, podemos mostrar la página del panel de control, y si el usuario no ha iniciado sesión, podemos redirigirlo a la página de inicio de sesión.

Añade el Redirect componente de la import declaración para React Router, y luego actualice el Dashboard componente para redirigir si no hay un token de acceso presente.

// src/App.js

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  Redirect, // Be sure to add this import
} from "react-router-dom";

// ...

function Dashboard() {
  function renderFn({ location }) {
    // If the user is not logged in, redirect to login
    if (!Userfront.accessToken()) {
      return (
        <Redirect
          to={{
            pathname: "/login",
            state: { from: location },
          }}
        />
      );
    }

    // If the user is logged in, show the dashboard
    const userData = JSON.stringify(Userfront.user, null, 2);
    return (
      <div>
        <h2>Dashboard</h2>
        <pre>{userData}</pre>
        <button onClick={Userfront.logout}>Logout</button>
      </div>
    );
  }

  return <Route render={renderFn} />;
}

Tenga en cuenta también que hemos agregado un botón de cierre de sesión llamando Userfront.logout() directamente:

<button onClick={Userfront.logout}>Logout</button>

Ahora, cuando un usuario inicia sesión, puede ver el panel de control. Si el usuario no ha iniciado sesión, será redirigido a la página de inicio de sesión.

Reaccionar la autenticación con una API

Probablemente desee recuperar información específica del usuario de su backend. Para proteger sus puntos finales de API, su servidor backend debe verificar que los JWT entrantes sean válidos.

Hay muchas bibliotecas disponibles para leer y verificar JWT en varios idiomas; aquí hay algunas bibliotecas populares para manejar JWT:

Node.js .RED Pitón Java

Tu token de acceso

Mientras el usuario está conectado, su token de acceso está disponible en su aplicación React como Userfront.accessToken().

Su aplicación React puede enviar esto como Bearer ficha dentro del Authorization encabezado a su servidor backend. Por ejemplo:

// Example of calling an endpoint with a JWT

import Userfront from "@userfront/react";
Userfront.init("demo1234");

async function getInfo() {
  const res = await window.fetch("/your-endpoint", {
    method: "GET",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${Userfront.accessToken()}`,
    },
  });

  console.log(res);
}

getInfo();

Para manejar una solicitud como esta, su backend debe leer el JWT de la Authorization encabezado y verifique que sea válido utilizando la clave pública que se encuentra en su panel de Userfront.

A continuación, se muestra un ejemplo de middleware Node.js para leer y verificar el token de acceso JWT:

// Node.js example (Express.js)

const jwt = require("jsonwebtoken");

function authenticateToken(req, res, next) {
  // Read the JWT access token from the request header
  const authHeader = req.headers["authorization"];
  const token = authHeader && authHeader.split(" ")[1];
  if (token == null) return res.sendStatus(401); // Return 401 if no token

  // Verify the token using the Userfront public key
  jwt.verify(token, process.env.USERFRONT_PUBLIC_KEY, (err, auth) => {
    if (err) return res.sendStatus(403); // Return 403 if there is an error verifying
    req.auth = auth;
    next();
  });
}

Con este enfoque, su servidor rechazará cualquier token no válido o faltante. También puede hacer referencia al contenido del token más adelante en los controladores de ruta utilizando el req.auth objeto:

console.log(req.auth);

// =>
{
  mode: 'test',
  tenantId: 'demo1234',
  userId: 5,
  userUuid: 'ab53dbdc-bb1a-4d4d-9edf-683a6ca3f609',
  isConfirmed: false,
  authorization: {
    demo1234: {
      tenantId: 'demo1234',
      name: 'Demo project',
      roles: ["admin"],
      permissions: []
    },
  },
  sessionId: '35d0bf4a-912c-4429-9886-cd65a4844a4f',
  iat: 1614114057,
  exp: 1616706057
}

Con esta información, puede realizar más comprobaciones si lo desea, o utilizar el userId o userUuid para buscar información de usuario para regresar.

Por ejemplo, si desea limitar una ruta solo a los usuarios administradores, puede verificar la authorization objeto del token de acceso verificado y rechace cualquier token que no tenga un admin papel:

// Node.js example (Express.js)

app.get("/users", (req, res) => {
  const authorization = req.auth.authorization["demo1234"] || {};

  if (authorization.roles.includes("admin")) {
    // Allow access
  } else {
    // Deny access
  }
});

Reaccionar SSO (inicio de sesión único)

Con sus formularios de Toolkit en su lugar, puede agregar proveedores de identidad social como Google, Facebook y LinkedIn a su aplicación React, o proveedores de identidad empresarial como Azure AD, Office365 y más.

Para ello, crea una aplicación con el proveedor de identidad (por ejemplo, Google) y luego agrega las credenciales de esa aplicación al panel de Userfront. El resultado es una experiencia de inicio de sesión modificada:

No se necesita código adicional para implementar el inicio de sesión único con este enfoque: puede agregar y eliminar proveedores sin actualizar sus formularios o la forma en que maneja los JWT.

Notas finales

Reaccionar la autenticación y el control de acceso puede ser complejo de hacer usted mismo, o puede ser simple cuando usa un servicio.

Tanto el paso de configuración como, lo que es más importante, el mantenimiento a lo largo del tiempo, se manejan con plataformas modernas como Userfront.

Los tokens web JSON le permiten separar claramente su capa de generación de tokens de autenticación del resto de su aplicación, lo que hace que sea más fácil razonar y más modular para las necesidades futuras. Esta arquitectura también le permite centrar sus esfuerzos en su aplicación principal, donde es probable que cree mucho más valor para usted o sus clientes.

Para obtener más detalles sobre cómo agregar autenticación a su aplicación React, visite la guía Userfront, que cubre todo, desde configurar sus formularios de autenticación hasta documentación de API, repositorios de ejemplo, trabajar con diferentes lenguajes y marcos, y más.

Cree un proyecto de Userfront gratuito