使用Auth0认证你的Next.js应用程序

786 阅读11分钟

认证,或确认用户在应用程序中的身份,在网络应用中的实施是至关重要的。认证建立了一个系统,通过用户名、密码、令牌、指纹、面部识别或安全问题来验证用户凭证。

开发人员经常在建立自己的认证系统或使用第三方服务之间纠结。在本文中,我们将探讨如何使用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

Auth0 Homepage New Project Framework

点击应用程序标签,选择你最近创建的应用程序。在你的设置标签上,滚动到应用程序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 组件看起来应该如下图所示。

Navbar Component

建立一个表单组件

在这一节中,我们将建立一个表单,让用户向他们的应用程序添加注释。我们将在表单组件中建立一个输入字段、一个提交按钮和一个函数来提交我们的笔记。

我们将使用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 ,它接收了titleidcontentsetRefresh 作为道具。使用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>
    );
};

在上面的代码块中,我们为用户添加了输入字段来添加注释。我们还添加了EditDelete 按钮。为了编辑一个输入,我们使用输入的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字符串存储在本地存储中。

我们的组件看起来应该如下图所示。

Nextjs Add Notes Component

编辑一个笔记

在这一节中,我们将创建一个函数,在笔记被添加到用户的本地存储后对其进行编辑。

**
 * 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方法,我们更新笔记的内容,替换或添加新的内容到一个笔记。

我们的编辑页面应该看起来像下面的图片。

Nextjs Notes Edit Component

渲染我们的应用程序

让我们导航到我们的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;

我们的应用程序应该看起来像下面的图片。

Final Application Auth0

结语

在这篇文章中,我们学习了如何在Next.js应用程序中设置Auth0进行认证。

我们使用Next.js和Auth0 Next.js SDK构建了一个Notes应用程序。然后,我们用Auth0开箱即用的签入、签出和电子邮件验证等功能添加了认证。

如果你正在寻找一种简单的方法来为你的项目添加认证,Auth0是一个伟大的工具。在这篇文章中,我们只是触及了不同验证方法的表面。最终,最适合你的解决方案将取决于你的项目需求。

本文中的代码的工作版本可以在CodeSandbox上找到

The postAuthenticate your Next.js application using Auth0appeared first onLogRocket Blog.