Jump to content

NextAuth.js

Когда вы хотите иметь систему аутентификации в вашем приложении Next.js, NextAuth.js - отличное решение, чтобы не заморачиваться с реализацией сложной безопасности самостоятельно. Он имеет обширный список провайдеров для быстрого добавления аутентификации OAuth и предоставляет адаптеры для многих баз данных и ORM.

Провайдер контекста

В точке входа вашего приложения вы увидите, что ваше приложение обернуто в SessionProvider:

pages/_app.tsx
<SessionProvider session={session}>
  <Component {...pageProps} />
</SessionProvider>

Этот провайдер контекста позволяет вашему приложению получить доступ к данным сессии из любого места вашего приложения, не передавая их как пропсы:

pages/users/[id].tsx
import { useSession } from "next-auth/react";

const User = () => {
  const { data: session } = useSession();

  if (!session) {
    // Handle unauthenticated state, e.g. render a SignIn component
    return <SignIn />;
  }

  return <p>Welcome {session.user.name}!</p>;
};

Получение сессии на сервере

Иногда вам может понадобиться запросить сессию на сервере. Чтобы сделать это, предварительно получите сессию с помощью функции-помощника getServerAuthSession, которую предоставляет create-t3-app, и передайте ее на клиент с помощью getServerSideProps:

pages/users/[id].tsx
import { getServerAuthSession } from "../server/auth";
import { type GetServerSideProps } from "next";

export const getServerSideProps: GetServerSideProps = async (ctx) => {
  const session = await getServerAuthSession(ctx);
  return {
    props: { session },
  };
};

const User = () => {
  const { data: session } = useSession();
  // NOTE: `session` wont have a loading state since it's already prefetched on the server

  ...
}

Включение user.id в сессию

create-t3-app настроен для использования session callback в конфигурации NextAuth.js для включения ID пользователя в объект session.

pages/api/auth/[...nextauth].ts
callbacks: {
    session({ session, user }) {
      if (session.user) {
        session.user.id = user.id;
      }
      return session;
    },
  },

Это связано с файлом объявления типов, чтобы убедиться, что user.id типизирован при доступе к объекту session. Подробнее о Module Augmentation в документации NextAuth.js.

types/next-auth.d.ts
import { DefaultSession } from "next-auth";

declare module "next-auth" {
  interface Session {
    user?: {
      id: string;
    } & DefaultSession["user"];
  }
}

Такой же шаблон может быть использован для добавления любых других данных в объект session, например, поля role, но не следует злоупотреблять для хранения конфиденциальных данных на клиенте.

Использование с tRPC

При использовании NextAuth.js с tRPC вы можете создавать повторно используемые, защищенные процедуры с помощью middleware. Это позволяет вам создавать процедуры, которые могут быть доступны только аутентифицированным пользователям. create-t3-app настраивает все это для вас, позволяя вам легко получать доступ к объекту сессии в аутентифицированных процедурах.

Это делается в два шага:

  1. Возьмите сессию из заголовков запроса с помощью функции getServerSession. Преимущество использования getServerSession вместо обычного getSession заключается в том, что это server-side функция и она не вызывает ненужных вызовов fetch. create-t3-app создает вспомогательную функцию, которая абстрагирует этот особый API.
server/auth.ts
export const getServerAuthSession = async (ctx: {
  req: GetServerSidePropsContext["req"];
  res: GetServerSidePropsContext["res"];
}) => {
  return await getServerSession(ctx.req, ctx.res, authOptions);
};

Используя эту вспомогательную функцию, мы можем получить сессию и передать ее в контекст tRPC:

server/api/trpc.ts
import { getServerAuthSession } from "../auth";

export const createContext = async (opts: CreateNextContextOptions) => {
  const { req, res } = opts;
  const session = await getServerAuthSession({ req, res });
  return await createContextInner({
    session,
  });
};
  1. Создайте tRPC middleware, которое проверяет, аутентифицирован ли пользователь. Затем мы используем middleware в protectedProcedure. Любой вызывающий эти процедуры должен быть аутентифицирован, иначе будет сгенерирована ошибка, которую можно правильно обработать на стороне клиента.
server/api/trpc.ts
export const protectedProcedure = t.procedure.use(({ ctx, next }) =>  {
  if (!ctx.session || !ctx.session.user) {
    throw new TRPCError({ code: "UNAUTHORIZED" });
  }
  return next({
    ctx: {
      // infers the `session` as non-nullable
      session: { ...ctx.session, user: ctx.session.user },
    },
  });
})

Обект сессии - это легкое, минимальное представление пользователя и содержит только несколько полей. При использовании protectedProcedures у вас есть доступ к идентификатору пользователя, который можно использовать для получения большего количества данных из базы данных.

server/api/routers/user.ts
const userRouter = router({
  me: protectedProcedure.query(async ({ ctx }) => {
    const user = await prisma.user.findUnique({
      where: {
        id: ctx.session.user.id,
      },
    });
    return user;
  }),
});

Использование с Prisma

Чтобы заставить NextAuth.js и Prisma работать вместе, необходимо большое количество начальной настройки. create-t3-app выполняет все это для вас, и если вы выберете одновременно и Prisma, и NextAuth.js, вы получите полностью работающую систему аутентификации со всеми предварительно настроенными, необходимыми моделями. Мы предоставляем вашему сгенерированому приложению предварительно настроеный провайдер Discord OAuth, который мы выбрали потому, с ним легче всего начать - просто укажите свои токены в .env и вы готовы к работе. Однако вы можете легко добавить больше провайдеров, следуя докуменетации NextAuth.js. Обратите внимание, что некоторые провайдеры требуют дополнительных полей для добавления в определенные модели. Мы рекомендуем вам прочитать документацию для провайдера, который вы хотите использовать, чтобы убедиться, что у вас есть все необходимые поля.

Добавление новых полей в ваши модели

Когда вы добавляете новые поля в любую из моделей User, Account, Session или VerificationToken (в большинстве случаев вам потребуется только изменить модель User), вам нужно иметь в виду, что адаптер Prisma автоматически создает поля в этих моделях при регистрации новых пользователей и входе в систему. Поэтому, добавляя новые поля в эти модели, вы должны предоставить значения по умолчанию для них, поскольку адаптер не знает о них.

Если например, вы хотите добавить role в модель User, вам нужно будет предоставить значение по умолчанию для поля role. Это делается путем добавления значения @default к полю role в модели User:

prisma/schema.prisma
+ enum Role {
+   USER
+   ADMIN
+ }

  model User {
    ...
+   role Role @default(USER)
  }

Использование с Next.js middleware

Использование NextAuth.js с Next.js middleware требует использования стратегии сеанса JWT для аутентификации. Это связано с тем, что middleware может получить доступ к сессионной cookie только в том случае, если это JWT. По умолчанию, create-t3-app настроен на использование default стратегии базы данных, в сочетании с Prisma в качестве адаптера базы данных.

Настраиваем DiscordProvider по умолчанию

  1. Перейдите в раздел Applications в Discord Developer Portal, и нажмите на “New Application”
  2. В меню настроек перейдите к “OAuth2 => General”
  • Скопируйте Client ID и вставьте его в DISCORD_CLIENT_ID в .env.
  • Возле Client Secret нажмите “Reset Secret” и скопируйте эту строку в DISCORD_CLIENT_SECRET в .env. Будьте осторожны, поскольку вы больше не сможете увидеть этот секрет, и сброс его приведет к тому, что существующий истечет.
  • Нажмите “Add Redirect” и вставьте <app url>/api/auth/callback/discord (пример для локальной разработки: http://localhost:3000/api/auth/callback/discord)
  • Сохраните изменения
  • Возможно, но не рекомендуется, использовать одно и то же приложение Discord для разработки и продакшена. Вы также можете рассмотреть Mocking the Provider во время разработки.

Полезные ресурсы

РесурсСсылка
Документация NextAuth.jshttps://next-auth.js.org/
NextAuth.js GitHubhttps://github.com/nextauthjs/next-auth
tRPC Kitchen Sink - with NextAuthhttps://kitchen-sink.trpc.io/next-auth