Se ver铆a asi, si es que usas los mismos estilos que este proyecto. 馃憖
馃敟 Creando los dise帽os de autenticaci贸n.
En esta aplicaci贸n no manejaremos rutas, solamente nos enfocamos en la autenticaci贸n. Asi que los dise帽os de login y register pueden ponerlos en vistas separadas si gustan, en mi caso solo ser谩n componentes.
Lo hago de esta manera para explicarlo de la manera m谩s simple.
馃敟 Dise帽o del inicio de sesi贸n.
Creamos la carpeta src/components y dentro creamos el archivo Login.tsx
El inicio de sesi贸n constara de 2 inputs:
Email.
Password.
Los input, deben de tener contener el atributo name identificando cada input.
Tambi茅n tendr谩 2 botones:
Inicio de sesi贸n normal.
Inicio de sesi贸n con Google.
Cada bot贸n debe tener su propiedad type esto es para que el bot贸n de Google no haga el posteo del formulario, solamente el primer bot贸n debe hacer eso.
exportconstLogin=()=>{return (<divclassName="container-auth"><h2>Login</h2><form><inputname="email"type="email"placeholder="E-mail"/><inputname="pass"type="password"placeholder="Password"/><divclassName="container-buttons"><buttontype="submit">Log In</button><buttontype="button"> Google </button></div></form></div>)}
馃敟 Dise帽o del registro.
El dise帽o del registro sera igual al del inicio de sesi贸n, con los mismos inputs pero solamente tiene un bot贸n
exportconstRegister=()=>{return (<divclassName="container-auth"><h2>Create an account</h2><form><inputname="email"type="email"placeholder="E-mail"/><inputname="pass"type="password"placeholder="Password"/><divclassName="container-buttons"><buttontype="submit">Sign up</button></div></form></div>)}
Ahora creamos un archivo barril, (index.ts) en la carpeta src/components para poder exportar nuestros componentes e importarlos en otro lugar de una manera mas ordenada.
export*from'./Login'export*from'./Register'
Una vez tengamos los dos dise帽os y el archivo barril, vamos a src/App.tsx y los agregamos:
Para manejar cada formulario, vamos a implementar un custom hook, lo llamaremos useForm.tsx y estar谩 dentro de la carpeta src/hooks.
Creamos una funci贸n que recibe como par谩metro un objeto que contenga el estado inicial del formulario y este tendr谩 un tipo gen茅rico, esto para que sea un poco mas reutilizable (por si quieren agregar mas campos a los formularios) el hook aunque en este caso no es necesario ya que tenemos los mismos campos en ambos formularios
Despu茅s, usaremos el estado para almacenar los valores del formulario. y con la funci贸n handleChange vamos a poder manejar los cambios del input y almacenar sus valores.
La funci贸n handleChange mandamos una propiedad computada a la funci贸n setForm y es por eso que es necesario el atributo name en el input, para identificarlo y obtener su valor.
Finalmente retornamos, mediante el operador rest esparcimos los valores del formulario, luego el formulario y finalmente la funci贸n handleChange.
Llamamos al hook useForm le mandamos un objeto que sera el estado inicial del formulario, en este caso tendremos dos propiedades que es el email y pass, que hacen referencia a los inputs que tenemos en el HTML. Sus valores por defecto son string vac铆os.
Desestructuramos las propiedades del formulario y la funci贸n handleChange.
En los inputs colocamos el atributo value con su correspondiente valor y el atributo onChange mandando la funci贸n handleChange para manejar el estado del input.
Finalmente, haremos una funci贸n llamada handleSubmit que recibe el evento del formulario, y esta funci贸n por el momento solo previene el comportamiento del formulario por defecto.
La funci贸n handleSubmit se la pasamos al atributo onSubmit de la etiqueta form.
import{useForm}from'../hooks/useForm';exportconstLogin=()=>{const{handleChange,pass,email}=useForm({initialState:{email:'',pass:''}})consthandleSubmit=(e:React.FormEvent<HTMLFormElement>)=>{e.preventDefault()}return (<divclassName="container-auth"><h2>Login</h2><formonSubmit={handleSubmit}><inputname="email"type="email"placeholder="E-mail"onChange={handleChange}value={email}/><inputname="pass"type="password"placeholder="Password"onChange={handleChange}value={pass}/><divclassName="container-buttons"><buttontype="submit">Log In</button><buttontype="button"> Google </button></div></form></div>)}
Asi como hicimos el Login.tsx es exactamente igual en el archivo Register.tsx
import{useForm}from"../hooks/useForm"exportconstRegister=()=>{const{handleChange,pass,email}=useForm({initialState:{email:'',pass:''}})consthandleSubmit=(e:React.FormEvent<HTMLFormElement>)=>{e.preventDefault()}return (<divclassName="container-auth"><h2>Create an account</h2><formonSubmit={handleSubmit}><inputname="email"type="email"placeholder="E-mail"onChange={handleChange}value={email}/><inputname="pass"type="password"placeholder="Password"onChange={handleChange}value={pass}/><divclassName="container-buttons"><buttontype="submit">Sign up</button></div></form></div>)}
鈿狅笍 Nota: Incluso pueden crear un 煤nico componente reutilizable, ya que los formularios son casi exactamente iguales solo se diferencian por los botones y la acci贸n de handleSubmit ya que esta funci贸n har谩 algo diferente dependiendo el formulario.
Ya tenemos el dise帽o y funcionalidad de los formularios, seguimos con la creaci贸n de nuestra app en Firebase.
馃敟 Configurar Firebase.
Ahora nos toca configurar la aplicaci贸n en firebase para poder usar su servicio de autenticaci贸n.
馃敟 Creando el proyecto.
1 - Vamos a la consola de firebase, iniciamos sesi贸n con alguna cuenta de correo de Gmail.
2 - Si es la primera vez que usan Firebase, les aparecer谩 un mensaje y bot贸n de crear proyecto el cual deben de presionar.
3 - Colocar el nuevo nombre al proyecto. En mi caso le puse auth-firebase-react.
Y al final hay un bot贸n de continuar que debes presionar.
4 - Esperar a que tu proyecto termine de crearse y despu茅s dar click en continuar
Una vez en continuar te va a mandar a un nuevo panel.
馃敟 Creando el la app.
1 - En el nuevo panel, tienes que identificar estos botones. Presiona el bot贸n de web para crear una app (el tercer bot贸n con fondo blanco).
2 - Coloca el nombre a tu app y dale click en Registrar app
3 - Una vez registrada la app, nos dar谩n nuestras credenciales, las cuales tenemos que guardar porque las vamos a usar despu茅s.
馃敟 Configurando la autenticaci贸n.
1 - Ahora, tenemos que regresar al panel, en el menu del lado derecho hay una opci贸n que dice compilaci贸n y dentro esta la opci贸n de autenticaci贸n y tienes que darle click, para que te lleve a otra pantalla.
Y tienes que dar click al bot贸n de Comenzar
2 - Despu茅s se te mostraran diversos proveedores, de los cuales vamos a seleccionar el de correo electr贸nico/contrase帽a y Google (puedes elegir los que quieras, aunque probablemente tendr谩s que hacer mas configuraci贸n).
Cuando elijas el de correo electr贸nico/contrase帽a solamente le das en Habilitar y al final viene un bot贸n de guardar que debes presionar una vez que termines de hacer alguna modificaci贸n en el proveedor.
Cuando elijas el de Google har谩s lo mismo que el anterior, y tambi茅n tendr谩s que seleccionar tu correo electr贸nico.
3 - Una vez habilitados los proveedores, tendr谩 que aparecerte en la pesta帽a Sign-in method de la siguiente forma.
4 - En la pesta帽a Users puedes ver todos los usuarios que se registren en tu aplicaci贸n.
馃敟 Configurando Firebase en nuestra aplicaci贸n de React.
De vuelta a nuestra app de React, vamos a instalar Firebase.
npm install firebase
creamos una nueva carpeta src/firebase y dentro un archivo llamado config.ts y pegamos toda la configuraci贸n que nos dieron en la secci贸n anterior
En mi caso, yo coloque los valores de cada propiedad en una variable de entorno, solamente creando en la ra铆z del proyecto un archivo .env.
Cada variable debe empezar con la palabra VITE_ para que funcionen.
VITE_APIKEY=1231231465
# more vars
Y para llamar a una variable de entorno tenemos que user el import.meta.env['Nombre de la variable']
Nota: tambi茅n debes notar que cambie el nombre de la variable app por FirebaseApp
Ahora para utilizar el servicio de autenticaci贸n de Firebase, usamos el m茅todo getAuth y tenemos que obtenerlo de 'firebase/auth', despu茅s le mandamos la inicializaci贸n de nuestra app, o sea la constante FirebaseApp
Ahora vamos a crear un nuevo archivo dentro de src/firebase llamado services.ts
Nota: todas las funciones de firebase que vamos a usar vienen de firebase/auth
馃敟 1 - Creando la funci贸n para autenticarse por Google.
Primero debemos crear una nueva instancia del proveedor que hayamos escogido, en este caso Google.
Luego creamos un m茅todo as铆ncrono, y dentro un try/catch porque ya sea que el usuario se equivoque o algo salga mal.
Mediante el m茅todo signInWithPopup, tendemos que mandarle nuestra instancia de FirebaseAuth, que ya hab铆amos creado en la secci贸n anterior, y la instancia del proveedor.
Si todo sale correcto, de la propiedad user de la variable resultado, te dar谩 varia informaci贸n como lo puedes ver en la desestructuraci贸n, pero solo vamos a usar el uid por eso lo retornamos.
Y en el catch, de hecho en todos los catch de este archivo, solo vamos a mandar una alerta con el mensaje que nos proporciona Firebase
馃敟 2 - Creando la funci贸n para autenticarse por credenciales.
La funci贸n para login y register usando credenciales son las mismas solo se diferencian por el m茅todo.
Ambas reciben un objeto que contiene el email y password y si todo sale bien, retornan el uid (estas funciones tambi茅n devuelven lo mismo que el de autenticarse con google, como displayName, photoURL, etc.)
Tanto la funci贸n de createUserWithEmailAndPassword y signInWithEmailAndPassword reciben la instancia de FirebaseAuth, y un email y password.
createUserWithEmailAndPassword, crea el usuario en Firebase.
signInWithEmailAndPassword, verifica si existe el usuario en Firebase.
馃敟 3 - Creando la funci贸n para observar los cambios en el estado de autenticaci贸n del usuario.
驴Por que queremos observar el estado de autenticaci贸n del usuario?
Bueno, supongamos que iniciamos sesi贸n correctamente, todo sale muy bien, estamos dentro de la application ya autenticados 馃ぉ. Pero upss! refrescamos el navegador y se nos pierde la sesi贸n, y tenemos que volver a iniciar sesi贸n 馃槬.
Asi que como resolvemos este problema, pues observando el estado de autenticaci贸n del usuario.
Para ello necesitamos un par de cosas.
Primero crear una funci贸n, que va a recibir como par谩metro un callback, o sea una funci贸n, dicha funci贸n nos ayudara a establecer el usuario autenticado o no autenticado.
Puedes notar en el c贸digo que usaremos un setter de useState y que ademas usaremos el Context API.El tipado les fallara porque aun no tenemos creado el context, asi que por el momento pueden colocar el tipo any.
Pero lo importante ahora es que recibimos la funci贸n setSession.
// type StateDispatch = React.Dispatch<React.SetStateAction<Pick<AuthStateContext, "status" | "userId">>>typeStateDispatch=anyexportconstonAuthStateHasChanged=(setSession:StateDispatch)=>{}
Ahora usaremos la funci贸n onAuthStateChanged, que recibe como primer par谩metro el FirebaseAuth
import{onAuthStateChanged}from'firebase/auth'import{FirebaseAuth}from'./config'// type StateDispatch = React.Dispatch<React.SetStateAction<Pick<AuthStateContext, "status" | "userId">>>typeStateDispatch=anyexportconstonAuthStateHasChanged=(setSession:StateDispatch)=>{onAuthStateChanged(FirebaseAuth)}
El segundo par谩metro es un callback, que retorna el usuario si es que existe su sesi贸n activa, de lo contrario retorna undefined.
import{onAuthStateChanged}from'firebase/auth'import{FirebaseAuth}from'./config'// type StateDispatch = React.Dispatch<React.SetStateAction<Pick<AuthStateContext, "status" | "userId">>>typeStateDispatch=anyexportconstonAuthStateHasChanged=(setSession:StateDispatch)=>{onAuthStateChanged(FirebaseAuth,user=>{})}
Evaluamos el usuario:
Si no existe, usamos el setSession para establecer el status en no-authenticated y el id del usuario en null. (no olviden colocar el return para evitar que se ejecute la siguiente linea)
Si existe, usamos el setSession para establecer el status en authenticated y el id del usuario.
import{onAuthStateChanged}from'firebase/auth'import{FirebaseAuth}from'./config'// type StateDispatch = React.Dispatch<React.SetStateAction<Pick<AuthStateContext, "status" | "userId">>>typeStateDispatch=anyexportconstonAuthStateHasChanged=(setSession:StateDispatch)=>{onAuthStateChanged(FirebaseAuth,user=>{if (!user)returnsetSession({status:'no-authenticated',userId:null})setSession({status:'authenticated',userId:user!.uid})})}
Probablemente no entiendas porque mandamos status o userId, bueno eso son los datos que necesitaremos en nuestro estado global, cuando vayamos a crear el contexto de nuestra app.
馃敟 4 - Creando la funci贸n para cerrar sesi贸n.
Ahora que pasa, gracias a que estamos observando el estado de autenticaci贸n del usuario, no podemos cambiar de usuario por un buen rato, ni aunque recargues o cierres el navegador.
Bueno para ello, debemos cerrar sesi贸n, y e muy sencillo:
Luego vamos a crear el estado inicial de nuestro contexto.
Por defecto el status estar谩 en checking por que al principio no sabemos si esta autenticado o no, lo sabremos una vez que se ejecuten ciertas funciones. Y tambi茅n el userId sera nulo por defecto hasta comprobar el estado de autenticaci贸n del usuario.
Ahora vamos a usar la funci贸n que creamos antes para observar el estado de autenticaci贸n del usuario.
Lo haremos en un efecto que solo se debe ejecutar la primera vez que inicie la aplicaci贸n. Y el callback que le mandaremos sera el setSession.
Luego haremos la funci贸n que ejecuta el cierre de sesi贸n. Llamamos a la funci贸n logoutFirebase y establecemos la session con el userId en nulo y el status en no-authenticated
Despu茅s, haremos una funci贸n que reutilizaremos en otras funciones, ya que queremos evitar repetir tanto c贸digo.
Esta funci贸n recibe el userId que puede ser un string o undefined, evaluamos si el userId es un string:
Si el userId es un string, significa que el usuario esta autenticado y establecemos la sesi贸n con el userId y el status en authenticated. (no olviden colocar el return para evitar que se ejecute la siguiente linea)
Si el userId es undefined, llamamos a la funci贸n handleLogOut, ya que el usuario no tiene una autenticaci贸n valida, y necesitamos cerrar todas las sesiones.
Las siguientes funciones, asi como esta ser谩n parecidas, primero hacer el checking, luego ejecutar el funci贸n especifico para esta tarea el cual nos retorna el userId o undefined y llamar el validateAuth mandando lo que retorna dicha funci贸n
Ahora necesitamos envolver nuestra app con el proveedor. Para ello vamos al punto mas alto de nuestra app que es el archivo src/main.tsx y agregamos el AuthProvider
Ahora nos vamos a el archivo src/components/Register.tsx y usamos nuestro contexto de la siguiente manera:
Importamos el hook useContext y le mandamos el AuthContext y obtenemos la funci贸n handleRegisterWithCredentials
Dicha funci贸n la ejecutamos dentro de handleSubmit y le mandamos el email y password.
Dicha funci贸n se va a ejecutar el atributo onClick de la etiqueta button de Google.
<buttontype="button"onClick={handleLoginWithGoogle}> Google </button>
Finalmente en nuestro archivo src/App.tsx
Vamos a usar el contexto extrayendo el status y el userID.
Evaluamos el status y si es checking, mostramos un loading.
const{status,userId}=useContext(AuthContext)if (status==='checking')return<pclassName="loading"><span>Checking credentials, wait a moment...</span></p>
Ahora al final del archivo crearemos dos componentes.
El primero es el HomePage (para simular que es un pagina diferente).
Este componente solo sera visible cuando el usuario este autenticado.
Y se mostrara el userID y un bot贸n que ejecuta el cerrar session.
exportconstHomePage=()=>{const{userId,handleLogOut}=useContext(AuthContext)return (<section><h5>Your ID is: <span>{userId}</span></h5><buttonclassName="btn-logout"onClick={handleLogOut}>Log out</button></section>)}
El segundo componente es el AuthPage, (simula otra pagina diferente).
Este componente solo sera visible cuando el usuario NO este autenticado.
Solo se muestran los componentes Login y Register que ten铆amos en nuestro componente App.
Ahora en el componente App, vamos hacer una validaci贸n. Donde si el status, es authenticated y existe el userId, mostramos el HomePage, de lo contrario que muestre el AuthPage
El archivo App.tsx quedar铆a de la siguiente manera 馃憖:
import{useContext}from"react"import{Login,Register}from"./components"import{AuthContext}from'./context/authContext';constApp=()=>{const{status,userId}=useContext(AuthContext)if (status==='checking')return<pclassName="loading"><span>Checking credentials, wait a moment...</span></p>return (<main><h1><b>Auth with</b><span>Firebase</span><b>and</b><span>React</span></h1>{(status==='authenticated'&&userId)?<HomePage/>:<AuthPage/>}</main>)}exportdefaultAppexportconstHomePage=()=>{const{userId,handleLogOut}=useContext(AuthContext)return (<section><h5>Your ID is: <span>{userId}</span></h5><buttonclassName="btn-logout"onClick={handleLogOut}>Log out</button></section>)}exportconstAuthPage=()=>{return (<section><Login/><Register/></section>)}
馃敟 Conclusi贸n.
Sin duda usar un servio como Firebase que nos ayuda mucho en ahorrar tiempo a comparaci贸n de construir nuestros propios servicios como la autenticaci贸n.
Espero que te haya gustado esta publicaci贸n y que te haya ayudada a entender m谩s sobre como realizar la autenticaci贸n de usuarios en tus aplicaci贸n con React. 馃
Si es que conoces alguna otra forma distinta o mejor de realizar esta funcionalidad con gusto puedes comentarla 馃檶.
Te invito a que revises mi portafolio en caso de que est茅s interesado en contactarme para alg煤n proyecto! Franklin Martinez Lucas
馃數 No olvides seguirme tambi茅n en twitter: @Frankomtz361