Site logo
Authors
  • avatar Nguyễn Đức Xinh
    Name
    Nguyễn Đức Xinh
    Twitter
Published on
Published on

Hướng dẫn tích hợp Auth0 vào ứng dụng NextJS để xác thực người dùng(Authentication)

Bảo mật ứng dụng của bạn là rất quan trọng. Sử dụng Auth0 giúp việc triển khai xác thực trở nên đơn giản với các giải pháp mạnh mẽ, linh hoạt và có thể tùy chỉnh. Hướng dẫn này sẽ giúp bạn thiết lập xác thực trong ứng dụng Next.js bằng Auth0.

Auth0 Là Gì?

Auth0 là một nền tảng xác thực và phân quyền, cung cấp quyền truy cập an toàn cho các ứng dụng và API. Nó hỗ trợ nhiều phương thức xác thực, bao gồm đăng nhập bằng mạng xã hội, single sign-on (SSO), và xác thực không dùng mật khẩu, làm cho Auth0 trở thành lựa chọn phổ biến cho các ứng dụng web hiện đại.

Đăng ký tài khoản Auth0

Bước đầu tiên là cần có 1 tài khoản Auth0. Nếu bạn đã có rồi thì có thể bỏ qua bước này.

Sau khi đăng ký xong thì bạn sẽ vào được màn hình Dashboard. Với role owner Admin thì sẽ hiển thị như sau:

Auth0 dashboard Admin role

Tạo Application mới.

  • Truy cập https://manage.auth0.com/dashboard

  • Chuyển đến Applications -> Applications -> Create Application và nhập thông tin như sau:

  • Name: Next Auth0

  • Choose an application type: Single Page Web Applications

Default Applications

Sau khi tạo xong App, Mở sang tab Settings lưu lại các thông tin sau dùng để cấu hình trong app NextJS.

  • Domain
  • Client ID
  • Client Secret

Default Applications

Cấu hình Auth0

Cấu hình Application URIs

Ở tab Settings, mục Application URIs bạn cần thêm URL của app của bạn. Ở demo này mình chạy Local NextJS nên sẽ có url là http://localhost:3000

  • Application Login URL: Để trống và nhập sau. Vì Auth0 yêu cầu https
  • Allowed Callback URLs: http://localhost:3000/auth/callback
  • Allowed Logout URLs: http://localhost:3000/auth/logout
  • Allowed Web Origins: http://localhost:3000

Default Applications

Nếu không đặt đúng giá trị, bạn sẽ gặp lỗi như sau:

Default Applications

Các phần còn lại bạn để mặc định.

Cross-Origin Authentication

Default Applications

Cấu hình Token Expiration

Default Applications

Cấu hình Refresh Token Rotation

Default Applications

Tạo example Next app

Đối với React, Mình recommend sử dụng Framework khi bắt đầu React app. Ở đây mình chọn NextJS. Bạn có thể xem chi tiết tại đây: https://react.dev/learn/start-a-new-react-project

Để cài đặt next bạn chạy lệnh sau và trả lời 1 số prompt:

npx create-next-app@latest example-nextjs

Default Applications

Truy cập next app và chạy ứng dụng bằng lệnh sau:

cd example-nextjs

npm run dev

Mở trình duyệt và nhập đường dẫn http://localhost:3000/ để xem kết quả NextJS

Default Applications

Cài đặt Auth0 Next.js SDK

Chạy lệnh sau trong thư mục dự án của bạn để cài đặt SDK Auth0 Next.js:

npm install @auth0/nextjs-auth0

# Đối với Next 15 bạn sử dụng v4 beta
npm i @auth0/nextjs-auth0@4.0.0-beta.0

Auth0 Next.js SDK Cùng cấp 1 số method để có thể tích hợp Auth0 vào Nextjs.

Cấu hình Auth0 Next.js SDK

Trước tiên bạn cần generate giá trị Secret bằng lệnh sau:

openssl rand -hex 32
# 5ba273ccf86990cec4e8a30f09859834c19aea094afc9f6bc0a5357fffbfabfa

Trong thư mục gốc của dự án, hãy thêm tệp .env.local với các biến môi trường sau:

AUTH0_SECRET='5ba273ccf86990cec4e8a30f09859834c19aea094afc9f6bc0a5357fffbfabfa'
APP_BASE_URL='http://localhost:3000'
AUTH0_CLIENT_ID='qBqi4MnLOOCsaaCkQOyCUHtqRWdqMxQH'
AUTH0_CLIENT_SECRET='QD-EI88JAB0SkwBXL-1yyGs86g4w6yXBYfZ4lLRoAYkhbwYPKSIJbL5fI3O862l6'
AUTH0_DOMAIN='dev-bvgptako.auth0.com'
  • AUTH0_SECRET: Giá trị Secret được sử dụng để mã hóa session cookie. Bạn có thể tạo chuỗi phù hợp bằng cách sử dụng openssl rand -hex 32 trên CLI như hướng dẫn ở trên.
  • APP_BASE_URL: URL của ứng dụng bạn đang chạy.
  • AUTH0_DOMAIN: URL của Auth0 tenant domain. Bạn lấy giá trị ở trong Auth0 Application -> Basic Information -> Domain. Nếu bạn sử dụng custom domain với Auth0, đặt giá trị này thành custom domain của bạn.
  • AUTH0_CLIENT_ID: Client ID của ứng dụng Auth0 của bạn. Bạn có thể lấy từ Auth0 Application -> Basic Information -> Slient Id.
  • AUTH0_CLIENT_SECRET: Client Secret. của ứng dụng Auth0 của bạn. Bạn có thể lấy từ Auth0 Application -> Basic Information -> Slient Secret.

Tạo Auth0 SDK client

Chúng ta sẽ tạo instance của Auth0 client. Instance này sẽ được import và sử dụng ở mọi nơi mà bạn cần truy cập các method authentication methods ở phía server.

Thêm nội dung vào file lib/auth0.ts như sau:

import { Auth0Client } from "@auth0/nextjs-auth0/server"

export const auth0 = new Auth0Client()

Thêm authentication middleware

Tạo file middleware.ts vào thư mục root của dự án. Nếu bạn sử dụng src, đường dẫn middleware.ts sẽ là src/middleware.ts

import type { NextRequest } from "next/server"

import { auth0 } from "./lib/auth0"

export async function middleware(request: NextRequest) {
  return await auth0.middleware(request)
}

export const config = {
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico, sitemap.xml, robots.txt (metadata files)
     */
    "/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
  ],
}

Lưu ý: ở SDK V4 sẽ không còn support dynamic API route handler: UserProvider, handleAuth

Sử dụng Auth0

Cập nhật trang Home bằng cách thay đổi nội dung tệp src/app/page.tsx như sau:

import { auth0 } from "@/lib/auth0"

export default async function Home() {
  const session = await auth0.getSession()

  if (!session) {
    return (
      <main className="flex gap-2 p-5">
        <a className="rounded-full border border-solid border-black/[.08] flex items-center justify-center text-sm h-10 px-4" href="/auth/login?screen_hint=signup">Sign up</a>
        <a className="rounded-full border border-solid border-black/[.08] flex items-center justify-center text-sm h-10 px-4" href="/auth/login">Log in</a>
      </main>
    )
  }

  return (
    <main>
      <h1>Welcome, {session.user.name}!</h1>
    </main>
  )
}

Lưu ý các link Log in, Sign up cần dùng thẻ <a> thay vì Link để đảm bảo routing không thực hiện bởi client-side. Vì điều đó có thể dẫn đến 1 số behavior không mong muốn.

Tiếp theo, mở trình duyệt tại đường dẫn: http://localhost:3000 để đến kiểm tra.

Default Applications

Khi người dùng click vào button Log in, sẽ chuyển hướng người dùng đến trang Auth0 Universal Login, Tại đây Auth0 có thể xác thực người dùng. Sau khi xác thực thành công, Auth0 sẽ chuyển hướng người dùng trở lại ứng dụng của bạn.

Default Applications

Ví dụ ở đây mình sẽ login bằng google.

Default Applications

Sau khi xác thực thành công sẽ chuyển hướng người dùng trở lại ứng dụng của bạn và tại đây ta có thể hiển thị thông tin người dùng.

Default Applications

Truy cập người dùng xác thực

Ở phần trước ta đã bao gồm phần hiển thị thông tin cơ bản của người dùng xác thực. Ngoài name thì bạn cũng có thể lấy thêm các thông tin khác như: email, picture, name, nickname, sub, email_verified, family_name,..

Đối với Server side - (App Router)

Trên server - App Router bạn dùng getSession helper

import { auth0 } from "@/lib/auth0"

export default async function Home() {
  const session = await auth0.getSession()
  const token = await auth0.getAccessToken()

  if (!session) {
    return <div>Not authenticated</div>
  }

  return (
    <main>
      <h1>Welcome, {session.user.name}!</h1>
    </main>
  )
}

Đối với Server side - (Pages Router)

Trên server - Pages Router bạn dùng getSession helper

import type { GetServerSideProps, InferGetServerSidePropsType } from "next"

import { auth0 } from "@/lib/auth0"

export const getServerSideProps = (async (ctx) => {
  const session = await auth0.getSession(ctx.req)
  const token = await auth0.getAccessToken(rectx.req)


  if (!session) return { props: { user: null } }

  return { props: { user: session.user ?? null } }
}) satisfies GetServerSideProps<{ user: any | null }>

export default function Page({
  user,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
  if (!user) {
    return (
      <main>
        <p>Not authenticated!</p>
      </main>
    )
  }

  return (
    <main>
      <p>Welcome, {user.name}!</p>
    </main>
  )
}

Đối với Client side(App Router)

Trên server bạn dùng hook **useUser() **

"use client"

import { useUser } from "@auth0/nextjs-auth0"
import { getAccessToken } from "@auth0/nextjs-auth0"

export default function Profile() {
  const { user, isLoading, error } = useUser()

  async function fetchData() {
    const token = await getAccessToken()
    // call external API with the token...
  }


  if (isLoading) return <div>Loading...</div>

  return (
    <main>
      <h1>Profile</h1>
      <div>
        <pre>{JSON.stringify(user, null, 2)}</pre>
      </div>
    </main>
  )
}

Tuỳ chỉnh the SDK client instance

// src/lib/auth0.ts
import { Auth0Client } from "@auth0/nextjs-auth0/server";

export const auth0 = new Auth0Client({
  signInReturnToPath: '/dashboard'
});

Xử lý lỗi

Lỗi - An error occured while trying to initiate the login request.

Nếu bạn gặp lỗi: An error occured while trying to initiate the login request. Hãy thử thay đổi như sau:

AUTH0_DOMAIN='https://dev-bvgptako.auth0.com'
AUTH0_DOMAIN='dev-bvgptako.auth0.com'

TroubleShooting.

Ngoài ra nếu có lỗi liên quan đến Auth0. hãy thử print: console.log('middleware', auth0); trong file middleware.ts để điều tra them

Kết Luận

Bằng cách làm theo các bước này, bạn có thể tích hợp Auth0 hiệu quả vào ứng dụng Next.js của mình, cung cấp trải nghiệm xác thực an toàn và liền mạch cho người dùng của bạn

Tài liệu tham khảo