How to set up secure authentication with Magic on both your client and server
How to set up secure authentication with Magic on both your client and server
Learn how to use email to create EVM wallets with Magic and Next.js.
Magic simplifies the process of securely authenticating users on both web and mobile clients while providing tools that allow your server to tailor user management according to specific requirements.
This guide will walk through how to use Magic on both the frontend and backend using the Magic SDK and the Magic Admin SDK, respectively. The provided code snippets are based on a Next.js web app utilizing serverless functions. You can adapt the frontend examples to work with most JavaScript frontend frameworks like React. Likewise, you can modify the backend examples to work with most JavaScript server-side frameworks such as Express.
#Project Setup
To follow along with this guide, you’ll need three things:
- A Magic Publishable API Key
- A Magic Secret API key
- A web client
- A server (or a client with serverless functions)
You can get your Publishable and Secret API Key from your Magic Dashboard.
If you already have an existing web client and server you plan to add Magic to, feel free to skip ahead to the section titled Add Magic to your web client. Otherwise, you can use the make-scoped-magic-app
CLI tool to bootstrap a Next.js app with Magic authentication already baked into the client.
The make-scoped-magic-app
CLI tool is an easy way to bootstrap new projects with Magic. To get started, simply run the command below in the shell of your choice. Be sure to replace <YOUR_PUBLISHABLE_API_KEY>
with the Publishable API Key from your Magic Dashboard.
You can also run the command without the flags to be guided interactively through the setup process. If you go through the guided prompts, note that this guide’s code snippets assume that you’ve chosen the “Quickstart” option when prompted. If you get stuck, take a look at our Quickstart guide!
01npx make-scoped-magic-app \
02 --template nextjs-dedicated-wallet \
03 --network polygon-mumbai \
04 --login-methods EmailOTP \
05 --publishable-api-key <YOUR_PUBLISHABLE_API_KEY>
The resulting project already contains all of the client-side code shown in the next section (Add Magic to your web client). Go through the next section to learn how the code is structured, but understand there’s no need to write additional code until we get to the section title Add Magic to your server.
#Add Magic to your web client
#Install Magic client-side SDK
To get started adding Magic to your project, install the magic-sdk
package with your preferred package manager. If you used the make-scoped-magic-app
CLI to bootstrap your project, this has already been done for you.
01npm install magic-sdk
#Initialize Magic
With the Magic SDK installed, you can initialize Magic with the Magic
constructor. This requires your Publishable API Key (found in your Magic dashboard). We prefer to add this to our .env
file rather than put it directly into our code.
01NEXT_PUBLIC_MAGIC_PUBLISHABLE_KEY=pk_live_1234567890
Where you initialize your Magic instance will depend on your chosen framework and architectural patterns. If you utilized the make-scoped-magic-app
CLI to initialize your project, this setup code has already been completed and can be found in src/components/magic/MagicProvider.tsx
, where Magic is initialized and surfaced to your app using the React Context API.
01import { Magic } from 'magic-sdk'
02
03const magic = new Magic(process.env.NEXT_PUBLIC_MAGIC_PUBLISHABLE_KEY, {
04 network: {
05 rpcUrl: "<https://rpc2.sepolia.org/>",
06 chainId: 11155111,
07 },
08})
This magic
variable will be your client’s access point for all things Magic. Take a look at the client-side API documentation for a list of modules and methods accessible through magic
.
#Authenticate users with Email OTP
Magic provides a number of ways to authenticate users. For simplicity, we’ll stick with one-time passcodes sent to the user’s email. For this, you’ll need to have a way for them to input their email address, after which you simply call loginWithEmailOTP
from Magic’s Auth module.
If the authentication is successful, the return value will be a token representing the user. You can then send this token to your server for identification purposes.
If you've generated a Next.js project using the Magic CLI, you will already have a login function created named handleLogin
in src/components/magic/auth/EmailOTP.tsx
. This function already calls loginWithEmailOTP
but does not send the token to the server. For the purposes of this guide, you’ll have to add a POST request to /api/login
similar to the code block below.
The /api/login
endpoint doesn't exist yet. We'll create it in the next section. If you’re using your own server, remember to replace the URL with the absolute URL to your server once you’ve created the endpoint.
01const handleLogin = async () => {
02 // handle email format validation and other potential errors
03
04 const didToken = await magic?.auth.loginWithEmailOTP({ email })
05
06 if (didToken) {
07 // initialize auth flow
08 const res = await fetch(`/api/login`, {
09 method: "POST",
10 headers: {
11 "Content-Type": "application/json",
12 Authorization: "Bearer " + didToken,
13 },
14 })
15 }
16}
#Add Magic to your server
Magic provides the Magic Admin SDK so you can customize user management for your app. This portion of the guide will walk you through basic token validation so you can verify on your server that users are who they say they are.
If you bootstrapped your project with the Magic CLI, everything in the section will be new and you’ll have to add it to the project yourself while going through each section.
#Install Magic Admin SDK
To install the Admin SDK, run the following installation command:
01npm install @magic-sdk/admin
Go to the Magic dashboard and copy your Magic secret key. You’ll need it to initialize Magic on your server. We like to add this to our .env
:
01MAGIC_SECRET_KEY=sk_live_1234567890
#Validate user tokens
In your server file, add your POST request for authentication. Since we’re using Next.js, we’ll do this by adding a serverless function file at pages/api/login.ts
.
The complete flow, starting with the client, is:
- User inputs their email
- A one-time code is sent to the user’s email
- User inputs the one-time code to authenticate
- Your client-side code will receive a DID token
- Your client-side code will send a POST request to
/api/login
and provide the user’s DID token in the authorization header as a bearer token - Your server-side code will use the Magic Admin SDK to validate the token and perform any custom backend logic
Where the Magic instance on the client was initialized with your Publishable API Key, the Magic instance on the server should be initialized with your Secret API key. The token validation can then be performed with magic.token.validate(didToken)
.
This is what that looks like when it’s all put together on the backend:
01import type { NextApiRequest, NextApiResponse } from "next"
02import { Magic } from "@magic-sdk/admin"
03
04type ResponseData =
05 | {
06 authenticated: boolean
07 }
08 | {
09 error: string
10 }
11
12export default function post(
13 req: NextApiRequest,
14 res: NextApiResponse<ResponseData>
15) {
16 const authHeader = req.headers.authorization ?? ""
17 if (authHeader === "") {
18 res.status(401).json({ error: "Missing authorization header" })
19 }
20
21 // creates a new Magic Admin instance for auth
22 const magic = new Magic(process.env.MAGIC_SECRET_KEY as string)
23
24 try {
25 // retrieves DID token from headers
26 const didToken = magic.utils.parseAuthorizationHeader(authHeader)
27 if (!didToken) {
28 throw new Error("Authorization header is missing")
29 }
30
31 // validates the Magic user's DID token
32 magic?.token.validate(didToken)
33 // custom user logic - e.g. save user info, session data, etc.
34
35 res.status(200).json({ authenticated: true })
36 } catch (error) {
37 console.log("Server Error: ", res.status(200))
38 res.status(500).json({ error: (error as Error).message })
39 }
40}
#Next Steps
You now know how to add simple Magic authentication to your web clients and validate user tokens from your server! Feel free to take a look at our final solution code or tinker with it directly in Codesandbox. These are only a few of Magic’s features and methods. Take a look at Magic’s API documentation for a detailed look at the SDK interface.
#Resources
- The full codebase can be found on GitHub
- Work with the project directly in Codesandbox
- Check out the API documentation