认证,或确认用户在应用程序中的身份,在网络应用中的实施是至关重要的。认证建立了一个系统,通过用户名、密码、令牌、指纹、面部识别或安全问题来验证用户凭证。
开发人员经常在建立自己的认证系统或使用第三方服务之间纠结。在本文中,我们将探讨如何使用Auth0在Next.js应用程序中实现认证。
什么是Auth0?
根据它的文档,"Auth0是一个灵活的、可插入的解决方案,可以为你的应用程序添加认证和授权服务"。基本上,Auth0允许你使用任何语言或堆栈向你的应用程序添加安全性。它的一些功能包括。
- 多因素认证:需要两个或两个以上的凭证才能访问(即双因素认证)
- 社交登录**:**允许你使用你的社交网络来注册和登录你的应用程序
- 单点登录:存储凭证,因此你不必在每次登录时输入凭证
- 分析:分析和报告工具
Auth0还配备了各种前端库的框架和SDK,开箱即用。其中一个是Auth0 Next.js SDK,它允许你使用Auth0向Next.js应用程序添加认证。
Auth0 Next.js SDK
首先,让我们从npm安装Auth0 Next.js SDK包。我们将根据我们独特的项目需求配置Auth0,然后我们就可以实施它了!
有了Auth0 Next.js SDK,我们可以使用客户端和服务器端的方法为我们的应用程序添加认证。在后端,我们将使用API路由。在前端,我们将使用React Context API。
安装Auth0
首先,[创建一个Auth0账户](http://React Context API)。导航到你的Auth0仪表板,点击应用程序。创建一个新的应用程序;我们将使用一个普通的Web应用程序。
你会得到一个弹出窗口,询问你使用的是哪个框架或堆栈。选择Next.js
。
点击应用程序标签,选择你最近创建的应用程序。在你的设置标签上,滚动到应用程序URI小节,并更新以下细节。
- 允许的回调URL:添加
[http://localhost:3000/api/auth/callback](http://localhost:3000/api/auth/callback)
- 允许的注销URL:添加
[http://localhost:3000/](http://localhost:3000/)
在上面的代码片段中,我们添加了一个回调URL,在用户登录到我们的应用程序后重定向,以及一个注销URL,在用户注销后重定向。
在我们的Auth0仪表板上,我们可以为我们的用户配置我们想要的认证类型,添加登录和注册页面,添加用户注册,甚至添加用户的数据库。现在,我们已经准备好创建我们的Next.js应用程序了!
创建Next.js应用程序
要初始化Next.js应用程序,我建议使用创建Next App。这个CLI工具会自动设置你的应用程序所需的一切。运行下面的命令来创建一个新项目。
npx create-next-app {project name}
另外,你也可以使用Yarn。
yarn create-next-app {project name}
接下来,cd
,进入项目目录,用下面的命令启动开发服务器。
cd {project name} && yarn run dev
为Next.js配置Auth0
现在,我们将在我们的应用程序中安装@auth0/next.js-auth0
的依赖。
npm install @auth0/nextjs-auth0
使用Yarn。
yarn add @auth/nextjs-auth0
接下来,让我们安装dotenv
包,我们将用它来存储我们的环境变量。我们还添加了styled-components,我们将用它来设计我们的应用程序的风格。
npm install dotenv styled-components
使用Yarn。
yarn add dotenv styled-components
在你项目的根目录下创建一个名为.env
的新文件,并添加以下由Auth0提供的凭证。
AUTH0_SECRET="your auth secret goes here"
AUTH0_BASE_URL="http://localhost:3000"
AUTH0_ISSUER_BASE_URL="your base URL from auth0"
AUTH0_CLIENT_ID="your unique ID goes here"
AUTH0_CLIENT_SECRET="your auth0 secret goes here"
AUTH0_SECRET
是一个用于加密cookie的32个字符的秘密,AUTH0_CLIENT_SECRET
可以在你的Auth0仪表板的设置标签下找到。
你可以在文档中阅读更多关于Auth0的配置。
构建应用程序组件和路由
在我们的Next.js应用程序的src
目录内创建一个名为components
的新文件夹。在components
文件夹内,创建一个名为Navbar
的新文件夹。在Navbar
,创建一个名为Navbar.jsx
的文件。
让我们创建一个名为Navbar
的功能组件,有两个动态链接,一个用于签入用户,另一个用于签出用户。
// components/Navbar/Navbar.jsx
import { useUser } from "@auth0/nextjs-auth0";
import styled from "styled-components";
const Navbar = () => {
const { user } = useUser();
return (
<Nav>
<h1>My Nextjs Note App</h1>
{!user ? (
<a href="/api/auth/login">Sign In</a>
) : (
<a href="/api/auth/logout">Sign Out</a>
)}
</Nav>
);
};
在上面的代码中,我们创建了一个名为Navbar
的功能组件。在它里面,我们初始化了一个nav
标签,并添加了一个带有我们应用程序的标题的h1
。然后,我们检查用户是否已经登录。如果他们登录了,我们就显示 "签出 "按钮。如果他们没有登录,我们就渲染一个登录按钮。
接下来,我们将向我们的Navbar
文件添加样式。
const Nav = styled.nav`
display: flex;
align-items: center;
justify-content: space-between;
width: 90%;
margin: 0 auto;
h1 {
font-size: 1.4rem;
font-style: oblique;
}
& > div {
display: flex;
a {
margin-left: 1rem;
}
}
a {
display: block !important;
border: none;
outline: none;
background: #5b6d5b;
color: #fff;
font-size: 1rem;
padding: 0.8rem 2.5rem;
border-radius: 5px;
transition: opacity 0.7s;
text-decoration: none;
&:hover {
opacity: 0.8;
}
}
`;
export default Navbar;
我们的Navbar
组件看起来应该如下图所示。
建立一个表单组件
在这一节中,我们将建立一个表单,让用户向他们的应用程序添加注释。我们将在表单组件中建立一个输入字段、一个提交按钮和一个函数来提交我们的笔记。
我们将使用localStorage
作为数据库来存储我们的笔记。
import { useState } from "react";
import styled from "styled-components";
import { addNote } from "../../utils/utils";
const Form = ({ hideForm, setRefresh }) => {
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const SubmitHandler = (e) => {
e.preventDefault();
title && content && addNote(title, content);
setTitle("");
setContent("");
hideForm(false);
setRefresh();
};
在上面的代码块中,我们从React导入了useState
Hook,并从utilities
目录中导入了addNote
对象。然后,我们创建了一个表单组件,它接收了一个hideForm
和一个setRefresh
道具。
接下来,我们添加了SubmitHandler
函数来处理提交我们的笔记。要提交一个笔记,它必须包含一个标题和内容。
现在,让我们建立一个组件和按钮,使用上面的函数提交我们的笔记。
return (
<FormWrapper onSubmit={SubmitHandler}>
<div>
<label htmlFor="title">Note Title</label>
<input
onChange={(e) => setTitle(e.target.value)}
value={title}
type="text"
name="note-title"
id="title"
placeholder="e.g About Today"
/>
</div>
<div>
<label htmlFor="content">Note Content</label>
<textarea
onChange={(e) => setContent(e.target.value)}
value={content}
name="note-content"
id="content"
placeholder="e.g I woke up 6am..."
/>
</div>
<button type="submit">Save Note</button>
</FormWrapper>
);
};
在上面的代码块中,我们创建了两个输入字段,一个用于添加笔记标题,一个用于用户输入内容。最后,我们添加了一个提交按钮。
为了完成我们的components
目录,我们需要建立一个笔记的组件。
构建笔记组件
现在,我们将编写一个函数,作为向我们的应用程序添加笔记的骨干。我们将初始化用户添加笔记后的编辑和删除功能。该组件将为我们的笔记接收一个ID,并将其作为我们应用程序的状态。
import { useState } from "react";
import styled from "styled-components";
import { editNote, deleteNote } from "../../utils/utils";
const Note = ({ title, id, content, setRefresh }) => {
const [edit, setEdit] = useState(false);
const [newTitle, setNewTitle] = useState(title);
const [newContent, setNewContent] = useState(content);
const submitHandler = (e) => {
e.preventDefault();
newTitle && newContent && editNote(id, newTitle, newContent);
setEdit(false);
setRefresh();
};
const deleteHandler = () => {
const ok = window.confirm("Are you sure you want to delete?");
if (ok) {
deleteNote(id);
setRefresh();
}
};
在上面的代码中,我们初始化了一个函数Note
,它接收了title
、id
、content
和setRefresh
作为道具。使用useState
钩子,我们将状态添加到title
,edit
, 和newContent
。就像我们对表单组件所做的那样,我们为用户创建了一个submitHandler
函数。
为了处理用户删除笔记的问题,我们创建了另一个名为deleteHandler
的函数,该函数在尝试删除笔记之前,会根据笔记ID
,要求用户进行确认提醒。
接下来,我们将建立一个Edit
,用于编辑我们的笔记,我们还将添加一个Delete
,用于使用条件语句删除帖子。
return (
<Wrapper>
{!edit ? (
<button
className="edit-btn"
onClick={() => setEdit(true)}
type="button"
>
Edit
</button>
) : null}
{!edit ? (
<button className="delete-btn" onClick={deleteHandler} type="button">
Delete
</button>
) : null}
{edit ? (
<form onSubmit={submitHandler}>
<h4>Edit Note</h4>
<div>
<input
onChange={(e) => setNewTitle(e.target.value)}
value={newTitle}
type="text"
name="note-title"
id="title"
placeholder="e.g About Today"
/>
</div>
<div>
<textarea
onChange={(e) => setNewContent(e.target.value)}
value={newContent}
name="note-content"
id="content"
placeholder="e.g I woke up 6am..."
/>
</div>
<button type="submit">Save</button>
</form>
) : (
<>
<h3>{title}</h3>
<p>{content}</p>
</>
)}
</Wrapper>
);
};
在上面的代码块中,我们为用户添加了输入字段来添加注释。我们还添加了Edit
和Delete
按钮。为了编辑一个输入,我们使用输入的ID
,更新了用户的注释。
注意屏幕上的错误,表明utils
没有被声明。我们将在我们的src
里面创建一个新的文件夹,叫做utils
,它将包含将用户的笔记存储在他们设备的本地存储中的逻辑。
存储和编辑笔记
让我们在utils
目录中编写这项任务的逻辑。
将用户笔记添加到本地存储。
/**
* Adds Note to list of Note in localStorage
* @param {*} title note title
* @param {*} content body of note
*/
export const addNote = (title, content) => {
let notesArr = JSON.parse(localStorage.getItem("next:note-app"));
if (notesArr?.length) {
const newNote = {
id: new Date().getTime(),
title,
content,
};
const newNotesArr = [...notesArr, newNote];
localStorage.setItem("next:note-app", JSON.stringify(newNotesArr));
} else {
const newNote = {
id: new Date().getTime(),
title,
content,
};
const newNotesArr = [newNote];
localStorage.setItem("next:note-app", JSON.stringify(newNotesArr));
}
};
在上面的代码中,我们导出了接收标题和内容的addNote
对象。接下来,我们初始化了该对象,并将其作为JSON传递给本地存储。为了确保用户不会添加空的笔记,我们添加了一个方法,监测用户笔记的数组长度,然后将其作为JSON字符串存储在本地存储中。
我们的组件看起来应该如下图所示。
编辑一个笔记
在这一节中,我们将创建一个函数,在笔记被添加到用户的本地存储后对其进行编辑。
**
* Edit Note Func
* @param {*} id note id
* @param {*} title new title
* @param {*} content new content
*/
export const editNote = (id, title, content) => {
let notesArr = JSON.parse(localStorage.getItem("next:note-app"));
let noteIndex = notesArr.findIndex((note) => note.id === id);
const selectedNote = notesArr[noteIndex];
const updatedNote = {
id: selectedNote.id,
title,
content,
};
notesArr.splice(noteIndex, 1, updatedNote);
localStorage.setItem("next:note-app", JSON.stringify(notesArr));
};
在上面的代码块中,我们将一个笔记解析为JSON文件,并添加到本地存储中。然后,我们通过传递笔记的ID
,检索出我们想要编辑的笔记。使用本地JavaScript的Splice方法,我们更新笔记的内容,替换或添加新的内容到一个笔记。
我们的编辑页面应该看起来像下面的图片。
渲染我们的应用程序
让我们导航到我们的src
文件夹中的api
目录。在里面,让我们更新index.js
来渲染我们的整个应用程序。
import Head from "next/head";
import { useState, useEffect } from "react";
import { useUser } from "@auth0/nextjs-auth0";
import styled from "styled-components";
import Form from "../components/Form/Form";
import Navbar from "../components/Navbar/Navbar";
import Note from "../components/Note/Note";
const App = () => {
const [showForm, setShowForm] = useState(false);
const [notes, setNotes] = useState([]);
const [refresh, setRefresh] = useState(false);
const { user, isLoading, error } = useUser();
useEffect(() => {
// Our notes from LocalStorage (our DB)
const store = JSON.parse(localStorage.getItem("next:note-app"));
setNotes(store);
}, [refresh]);
return (
<>
<Head>
<title>Next.js note app with auth0</title>
<meta
name="description"
content="A basic crud project to illustrate how to use autho in a next.js app"
/>
<link rel="icon" href="/favicon.ico" />
</Head>
<Wrapper>
<Navbar />
{isLoading ? (
<p className="nodata-indicator">Loading...</p>
) : error ? (
<p>{error?.message}</p>
) : user ? (
<>
{showForm ? (
<article className="form-wrapper">
<Form
setRefresh={() => setRefresh(!refresh)}
hideForm={setShowForm}
/>
</article>
) : (
<article className="form-wrapper">
<button onClick={() => setShowForm(!showForm)} type="button">
Add note
</button>
</article>
)}
{!notes?.length ? (
<div>
<p className="nodata-indicator">
No notes available, Click on the button above to add
</p>
</div>
) : (
<article className="notes-wrapper">
{notes.map((note) => {
return (
<Note
key={note.id}
id={note.id}
title={note.title}
content={note.content}
setRefresh={() => setRefresh(!refresh)}
/>
);
})}
</article>
)}
</>
) : (
<div>
<p className="non-auth-text">
Welcome to my next.js note app, sign in to get started
</p>
</div>
)}
</Wrapper>
</>
);
};
在上面的代码块中,我们正在使用useEffect
钩子从我们的本地存储中获取笔记,作为我们的数据库,并将它们渲染为我们的默认笔记。
我们还为我们的应用程序添加了一个标题。使用Auth0,我们添加了登录功能,让用户登录到他们的个性化笔记应用程序。我们还创建了一个用于添加笔记的按钮。为了呈现我们所有的笔记,我们使用了本地JavaScript地图对象。
为了完成我们的应用程序,让我们添加一些样式。
const Wrapper = styled.section`
margin: 1rem 0;
max-width: 100%;
overflow-x: hidden;
height: 100%;
.form-wrapper {
max-width: 60%;
margin: 1.5rem auto 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
button {
border: none;
outline: none;
background: #5b6d5b;
color: #fff;
font-size: 1rem;
height: 2.6rem;
width: 10rem;
border-radius: 5px;
transition: opacity 0.7s;
&:hover {
opacity: 0.8;
}
}
}
.notes-wrapper {
max-width: 95%;
margin: 4rem auto;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 2rem;
}
.nodata-indicator {
margin-top: 4rem;
text-align: center;
}
.non-auth-text {
margin-top: 4rem;
text-align: center;
font-size: 1.5rem;
}
`;
export default App;
我们的应用程序应该看起来像下面的图片。
结语
在这篇文章中,我们学习了如何在Next.js应用程序中设置Auth0进行认证。
我们使用Next.js和Auth0 Next.js SDK构建了一个Notes应用程序。然后,我们用Auth0开箱即用的签入、签出和电子邮件验证等功能添加了认证。
如果你正在寻找一种简单的方法来为你的项目添加认证,Auth0是一个伟大的工具。在这篇文章中,我们只是触及了不同验证方法的表面。最终,最适合你的解决方案将取决于你的项目需求。
本文中的代码的工作版本可以在CodeSandbox上找到。
The postAuthenticate your Next.js application using Auth0appeared first onLogRocket Blog.