---
tags: learn
---
# Vercel NextJS
https://vercel.com/guides/add-auth-to-nextjs-with-magic
## What's Missing:
1. Missing a Github repo to be cloned and follow along at home
- threfore I've had to create the folders/directories from scratch
- stage the Github repo with commits, and go through the different tutorial and each step is one step
- you add some code to step 1, edit the code in step 2, and step 3 even more code, final version is on github website
Github repo tutorial
with my own markdown, with new tutorial
2. make sure dev is installed
``` npm run dev ```
3. no longer use "pk_test"
---
## Clarification:
### Confusion 1
"Grab both the publishable and secret keys and place them in the environment (.env.local) file like below. Also, we'll add ENCRYPTION_SECRET, which will be used for encryption. You should create your own secret."
Unclear to me if I am creating my own file
``` loginWithMagicLink ```
Within a Magic package
``` Magic.loginWithMagicLink ```
within Magic object, there is an auth attribute that has a login method, it's not directly calling Magic.loginWithMagicLink but it's going through auth and a Magic instance
---
### Confusion 2

This could be more clear, of what should be copy & pasted
ie this
```python=
// the magic code
const did = await new Magic(
process.env.NEXT_PUBLIC_MAGIC_PUB_KEY,
).auth.loginWithMagicLink({ email: elements.email.value });
// Once we have the did from magic, login with our own API
const authRequest = await fetch('/api/login', {
method: 'POST',
headers: { Authorization: `Bearer ${did}` },
});
if (authRequest.ok) {
// We successfully logged in, our API
// set authorization cookies and now we
// can redirect to the dashboard!
router.push('/dashboard');
} else {
/* handle errors */
}
};
```
should replace this:
```
// Add the Magic code here
// Once we have the token from magic,
// update our own database
// const authRequest = await fetch()
// if (authRequest.ok) {
// We successfully logged in, our API
// set authorization cookies and now we
// can redirect to the dashboard!
// router.push('/dashboard')
// } else { /* handle errors */ }
};
```
---
### Confusion 3

so this portion confuses me, as to what the guide wants me to do here
https://vercel.com/guides/add-auth-to-nextjs-with-magic#issue-an-authorization-token
but also the guide says, for this exercise we also won’t be doing something
basically what this section is saying is: "hey you don't need to anything yet, this is just a place holder"
1. First step is DID
2. send step is api/login.jS - using DID and passing it in the body. The body is usually given in a JSON format

this is the updated codebase once you fill out the template from the original template at the beginning of the tutorial
this is the step to integrate the API, by copy & pasting below the comment
```javascript=
const did = magic.utils.parseAuthorizationHeader(req.headers.authorization);
const user = await magic.users.getMetadataByToken(did);
```

---
### Confusion 4
Rather than using hapi/iron, might consider which might simplify the code a bit:
https://github.com/vvo/iron-session
---
### Confusion 5
It isn’t clear if the recommendation is client-side auth or server-side auth
API is always run on the server-side
// pages/api/login.js
// pages/login.js
is what you download onto your browser and that's client-side
one line of code for the client-side
1. Create a cookies.js
https://vercel.com/guides/add-auth-to-nextjs-with-magic#persisting-authorization-state
---
# Run the program
ensure that the setup.sh and run.sh are in the src directory
setup SH Script
create a setup.sh in the top directory and copy over all the install terminal commands such as:
```
yarn add magic-sdk
yarn add @hapi/iron
yarn add swr
npm install dev
```
and then create *run.sh*
include ```npm run dev```
Errors after running the application

need to import Magic
article assumes I had known to import Magic
```python=
// pages/login.js
import { Magic } from 'magic-sdk';
```

expose the hidden ```.env.local``` file in the src directory, if you have a Mac then it's
cmd+.+shift

do not publish private key in github
replace encryption secret with 123456
control+c to stop the server
and rerun the sh run.sh to rerun the server reloading the environmental variables
does environmental variables
1. Replace
``` process.env.NEXT_PUBLIC_MAGIC_PUB_KEY ``` in //pages/login.js with my public API Key with one quote not double quote

https://magic.crisp.help/en/article/internal-server-error-trace-id-jywvam/
---
1.
in //pages/index.js i had replaced <link> with <a> but move it back to </link> and import link
2. dependencies in package.json
3. update encrypton key up to 32 characters
Cookies error
---
# Josh's feedback
---
Resources
8 hours to debug 100 lines of code
How to upload package
https://www.google.com/search?q=upload+a+javascript+package+to+npm&ei=APDaY9GYG_7m5NoP0NqW8A8&ved=0ahUKEwjRmIvFt_X8AhV-M1kFHVCtBf4Q4dUDCBA&uact=5&oq=upload+a+javascript+package+to+npm&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCCEQoAEyCwghEBYQHhDxBBAdOgoIABBHENYEELADOgUIIRCrAjoICCEQFhAeEB1KBAhBGABKBAhGGABQgQpYhRBg4xBoAXABeACAAV2IAe4BkgEBM5gBAKABAcgBCMABAQ&sclient=gws-wiz-serp
---
# Josh's Write
Magic is a plug and play SDK that supports a variety of passwordless login methods, including email magic links, WebAuthn, and social login - Facebook, Google, Twitter, LinkedIn, Discord and more. It allows companies to easily and securely onboard and authenticate users, creating non-custodial wallets for them in the process.
In this guide, we’re going to take a look at how to integrate Magic with Next.js, using Magic’s “magic link” login and utilizing Next.js’ API routes for our validation. We'll also look at how to use [React’s Context hook](https://reactjs.org/docs/context.html) to ensure a fluid user experience for our app.
Let's start by forking and cloning down [this starter code](https://github.com/magiclabs/vercel-magic-guide-boilerplate). This repo was created by using `npx create-next-app` and has all the starter files you’ll need, along with some added stylings. You'll want to be fairly familiar with how [Next.js](https://nextjs.org/docs/basic-features/pages) works before going any further (if not, check out their [tutorial](https://nextjs.org/learn)). As we work through tutorial, here is what we want our user flow to look like:
1. A user visits our app, enters their email address, and clicks the ***************Send Magic Link*************** button.
2. They click the link sent to their email and Magic authenticates them.
3. Our app stores their metadata and redirects them to the *********dashboard********* page.
4. The user is able to view their account.
# Repositories
**Starter Code: https://github.com/magiclabs/vercel-magic-guide-boilerplate**
*Completed Code for reference: https://github.com/magiclabs/vercel-magic-guide*
# Getting started
Once you have forked and cloned the starter repo, you’ll want to take the following steps:
1. Install the dependencies
```bash
npm install
# or
yarn install
```
1. Run the development server
```bash
npm run dev
# or
yarn dev
```
1. Open [http://localhost:3000](http://localhost:3000/) with your browser to see the result.
# Creating the first pages
First we’re going to get our context set up so that our user information is available throughout our app. Navigate to `lib/UserContext.js` and add the following code to create our Context object.
```jsx
// lib/UserContext.js
import { createContext } from "react";
export const UserContext = createContext(null);
```
Then navigate to `pages/_app.js` so that we can wrap our app in the Provider component and initialize our `user` state. This will make it so all of our pages have access to `user` and `setUser`.
```jsx
// pages/_app.js
import "@/styles/globals.css";
import { useState } from "react";
import { UserContext } from "@/lib/UserContext";
export default function App({ Component, pageProps }) {
const [user, setUser] = useState();
return (
<UserContext.Provider value={[user, setUser]}>
<Component {...pageProps} />
</UserContext.Provider>
);
}
```
Next, we’re going to create the scaffolding for our `login` and `dashboard` pages. Open up `pages/login.js` and add the following.
```jsx
// pages/login.js
import { useContext, useState } from "react";
import { UserContext } from "@/lib/UserContext";
export default function Login() {
// Allows us to access the user state and update it within this page
const [user, setUser] = useContext(UserContext);
// Our email state that we'll use for our login form
const [email, setEmail] = useState("");
const handleLogin = async (e) => {
e.preventDefault();
// We'll fill in the rest of this later
};
return (
<form onSubmit={handleLogin}>
<label htmlFor="email">Email</label>
<input
name="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<button type="submit">Send Magic Link</button>
</form>
);
}
```
Then open up `pages/dashboard.js`. This will be the page our user sees once they’ve signed in through Magic.
```jsx
// pages/dashboard.js
import { useContext } from "react";
import { UserContext } from "@/lib/UserContext";
export default function Dashboard() {
const [user, setUser] = useContext(UserContext);
const logout = () => {
// We'll fill this out later
};
return (
<>
{/* We check here to make sure user exists before attempting to access its data */}
{user?.issuer && (
<>
<h1>Dashboard</h1>
<h2>Email</h2>
<p>{user.email}</p>
<h2>Wallet Address</h2>
<p>{user.publicAddress}</p>
<button onClick={logout}>Logout</button>
</>
)}
</>
);
}
```
Lastly, we’re going to add some simple loading functionality so our users don’t see a blank page while our app loads. Add the following to `pages/index.js`.
```jsx
// pages/index.js
import { useContext } from "react";
import { UserContext } from "@/lib/UserContext";
export default function Home() {
// Allow this component to access our user state
const [user] = useContext(UserContext);
return <div>
{/* Check to see if we are in a loading state and display a message if true */}
{user?.loading && <p>Loading...</p>}
</div>;
}
```
# Set up Magic integration
Our first step in integrating Magic is by creating an account, which you can do by heading over to [magic.link](https://dashboard.magic.link/login). You’ll then create a new **********Magic Auth********** app, which will provide you with your API keys as seen below.

In your codebase, rename `.env.local.example` to `.env.local` and add your publishable API key and secret key to it.
```jsx
// .env.local
NEXT_PUBLIC_MAGIC_PUBLISHABLE_KEY="YOUR_PUBLISHABLE_API_KEY"
MAGIC_SECRET_KEY="YOUR_SECRET_KEY"
```
We are then going to create a helper function that we will use to create our Magic instances. This instance will allow us access to all of Magic’s methods and connect us to the Ethereum Network ([Magic allows us to connect to 20+ blockchains](https://magic.link/docs/auth/overview#blockchains)). Navigate to `lib/magic.js`.
```jsx
// lib/magic.js
import { Magic } from "magic-sdk";
const createMagic = (key) => {
// We make sure that the window object is available
// Then we create a new instance of Magic using a publishable key
return typeof window !== "undefined" && new Magic(key);
};
// Pass in your publishable key from your .env file
export const magic = createMagic(process.env.NEXT_PUBLIC_MAGIC_PUBLISHABLE_KEY);
```
We are now able to easily create new instances of Magic wherever needed, keeping our code DRY.
# Authenticate with Magic
Magic is built using principles of distributed security. They run a secure in-house operation that delegates most of the security storage to Amazon's Hardware Security Module or HSM (you can learn more about HSMs and how Magic uses them in their [security documentation](https://docs.magic.link/security)). Not even Magic employees have access to the HSM. They've locked everyone out of ever getting access to the keys stored there.
Since Magic runs in this distributed manner, their authentication returns a **[decentralized identifier](https://magic.link/docs/auth/introduction/decentralized-id)**. This identifier can be exchanged with Magic for information about the user. Once we have that **DID,** we know that Magic has successfully authenticated that user and our app can take over.
To start, let’s take care of our login API endpoint which we’ll be using shortly in our `login` page. This will be how we validate the user’s **DID** token and issue an authentication token in return. We’ll be using [Magic’s server-side SDK](https://magic.link/docs/auth/api-reference/server-side-sdks/node) for this.
```jsx
import { Magic } from "@magic-sdk/admin";
// Create an instance of magic admin using our secret key (not our publishable key)
let mAdmin = new Magic(process.env.MAGIC_SECRET_KEY);
export default async function login(req, res) {
try {
// Grab the DID token from our headers and parse it
const didToken = mAdmin.utils.parseAuthorizationHeader(
req.headers.authorization
);
// Validate the token and send back a successful response
await mAdmin.token.validate(didToken);
res.status(200).json({ authenticated: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
```
Now let’s go back and finish our `login` page. We’re going to be filling out the `handleLogin` function by utilizing our new endpoint that we created in `pages/api/login.js`. We’ll also add a `useEffect` hook to check if a user is already logged in, and if so, route them to the dashboard.
Within our `handleLogin` function,`loginWithMagicLink` will send an email to the user and they will follow a secure authentication process outside of our application.
Once the user has successfully authenticated with Magic, they'll be instructed to return to our app. At that point, Magic will return a **DID** which we can use as a token in our application. This token will then be sent to our server-side implementation of Magic’s SDK where we can validate it and issue an authorization token so that the user has access to our app. Upon receiving a successful response, we retrieve the user’s metadata, store it in our user context, and route them to the dashboard.
```jsx
// pages/login.js
import { useContext, useState, useEffect } from "react";
import { UserContext } from "@/lib/UserContext";
import { useRouter } from "next/router";
import { magic } from "@/lib/magic";
export default function Login() {
const [user, setUser] = useContext(UserContext);
const [email, setEmail] = useState("");
// Create our router
const router = useRouter();
// Make sure to add useEffect to your imports
useEffect(() => {
// Check for an issuer on our user object. If it exists, route them to the dashboard.
user?.issuer && router.push("/dashboard");
}, [user]);
const handleLogin = async (e) => {
e.preventDefault();
// Log in using our email with Magic and store the returned DID token in a variable
try {
const didToken = await magic.auth.loginWithMagicLink({
email,
});
// Send this token to our validation endpoint
const res = await fetch("/api/login", {
method: "POST",
headers: {
"Content-type": "application/json",
Authorization: `Bearer ${didToken}`,
},
});
// If successful, update our user state with their metadata and route to the dashboard
if (res.ok) {
const userMetadata = await magic.user.getMetadata();
setUser(userMetadata);
router.push("/dashboard");
}
} catch (error) {
console.error(error);
}
};
return (
<form onSubmit={handleLogin}>
<label htmlFor="email">Email</label>
<input
name="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<button type="submit">Send Magic Link</button>
</form>
);
}
```
# Persisting authorization state and logging out
By default, Magic allows users to remain authenticated for up to 7 days, so long as they don’t logout or clear their browser data ([this can be extended up to 90 days with Magic Auth Plus](https://magic.link/docs/auth/more/customization/session-management)). In our case, once a user has been authenticated, we don’t want them to have to repeat this login process every time they leave our site and come back. To accomplish this, we are going to utilize another `useEffect` along with Magic’s `isLoggedIn` method, which will return true if they have an authenticated token stored in cookies, and false if not. Open up `pages/_app.js` and add the following.
```jsx
// pages/_app.js
import "@/styles/globals.css";
import { useState, useEffect } from "react";
import { UserContext } from "@/lib/UserContext";
import { useRouter } from "next/router";
import { magic } from "@/lib/magic";
export default function App({ Component, pageProps }) {
const [user, setUser] = useState();
// Create our router
const router = useRouter();
useEffect(() => {
// Set loading to true to display our loading message within pages/index.js
setUser({ loading: true });
// Check if the user is authenticated already
magic.user.isLoggedIn().then((isLoggedIn) => {
if (isLoggedIn) {
// Pull their metadata, update our state, and route to dashboard
magic.user.getMetadata().then((userData) => setUser(userData));
router.push("/dashboard");
} else {
// If false, route them to the login page and reset the user state
router.push("/login");
setUser({ user: null });
}
});
// Add an empty dependency array so the useEffect only runs once upon page load
}, []);
return (
<UserContext.Provider value={[user, setUser]}>
<Component {...pageProps} />
</UserContext.Provider>
);
}
```
Now that we’re able to authenticate our user and persist this state, we are going to give them the ability to log out. This is a quick and easy final step with Magic. Open up `pages/dashboard.js` and add the following.
```jsx
// pages/dashboard.js
import { useContext } from "react";
import { UserContext } from "@/lib/UserContext";
import { magic } from "@/lib/magic";
import { useRouter } from "next/router";
export default function Dashboard() {
const [user, setUser] = useContext(UserContext);
// Create our router
const router = useRouter();
const logout = () => {
// Call Magic's logout method, reset the user state, and route to the login page
magic.user.logout().then(() => {
setUser({ user: null });
router.push("/login");
});
};
return (
<>
{user?.issuer && (
<>
<h1>Dashboard</h1>
<h2>Email</h2>
<p>{user.email}</p>
<h2>Wallet Address</h2>
<p>{user.publicAddress}</p>
<button onClick={logout}>Logout</button>
</>
)}
</>
);
}
```
And that’s it! Our app is now able to create new wallets and securely authenticate users, all without passwords.
# Next steps
Head over to the [Magic docs](https://magic.link/docs/auth/overview) and experiment with integrating other features of the Magic SDK. You could try adding in login via SMS or maybe some social logins. Also, by incorporating either the [Ethers.js](https://docs.ethers.org/v5/) or [Web3.js](https://web3js.readthedocs.io/en/v1.8.2/) libraries, you can add in the functionality for your users to sign and send transactions.