The process to create a magic link is:
terminal🚶 [1] Sends email address 🔥 User ------------------------------> Firebase | | [2] Token generation and | sending magic link back Email <-----------------------------+ | | [3] Click on magic link | 🔑 +-----------------------------------> [4] signInWithCustomToken
We'll create a component containing:
To get the FIREBASE_MAGIC_LINK_FUNCTION_URL
, go to next step.
jsximport React, { useEffect, useState } from 'react'; import { auth } from './Firebase'; const Login = () => { const [email, setEmail] = useState(''); const sendEmail = async () => { let url = 'FIREBASE_MAGIC_LINK_FUNCTION_URL'; let response = await fetch(url, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({email: email}) }); if(response.ok){ console.log('Magic link sent to the user'); } } return( <div className = 'MagicLink'> <ul> <input placeholder = 'Email...' onChange = {e => setEmail(e.target.value)}></input> <button onClick = {sendEmail}>Send magic link</button> </ul> </div> ); } export default Login;
We need to define a function in Firebase that creates a custom token depending on the email received.
Then, it will generate a magic link using email and token. The URL of the magic link will be such as https://YOUR_DOMAIN.com/v/email=${email}&token=${token}
.
Now it's time to configure a custom email template. We'll send the custom email to the user using Nodemailer.
After deploying the function, remember to edit FIREBASE_MAGIC_LINK_FUNCTION_URL
in the prior component.
javascriptexports.sendMagicLink = functions.https.onRequest((request, response) => { const cors = require('cors')({origin: true}); const nodemailer = require('nodemailer'); return cors(request, response, async () => { let email = request.body.email; let token = await admin.auth().createCustomToken(email); let magicLink = `https://YOUR_DOMAIN.com/v/email=${email}&token=${token}`; const mailTransport = nodemailer.createTransport({ service: 'gmail', auth: { user: user, pass: pass, } }); const mailOptions = { from: 'Erik 🤖', to: email, subject: 'Welcome', html: `<p>Hello,</p> <p>Click on this <a href = '${magicLink}'>link</a> to verify your account.</p> <p>Thanks,</p> <p>Erik</p>` }; await mailTransport.sendMail(mailOptions); response.sendStatus(200); }); });
If everything went as expected, the user will receive an email on his inbox.
If the user clicks on the received magic link, he'll be redirected to a URL such as https://YOUR_DOMAIN.com/v/email=${email}&token=${token}
.
We'll create a component to read the URL parameters and we'll use firebase.auth().signInWithCustomToken()
to sign-in and to verify the token as well.
jsximport React, { useEffect, useState } from 'react'; import firebase, { auth } from './Firebase'; const Verify = () => { const [status, setStatus] = useState('processing'); useEffect(() => { let object = getURLparams(); let email = object.email; let token = object.token; signIn(token); }, []); const getURLparams = () => { let str = window.location.pathname.split('/').pop(); let arr = str.split('&'); let entries = arr.map(str => [str.split('=')[0], str.split('=')[1]]); let object = Object.fromEntries(entries); return object; } const signIn = async (token) => { try{ await firebase.auth().signInWithCustomToken(token); setStatus('logged'); } catch(e){ setStatus('error'); } } return( <div className = 'Verify'> { status === 'processing' ? 'Processing' : status === 'logged' ? 'Logged in' : status === 'error' ? 'Error' : null } </div> ); } export default Verify;
Hi, I'm Erik, an engineer from Barcelona. If you like the post or have any comments, say hi.