NextAuth.js
Когда вы хотите иметь систему аутентификации в вашем приложении Next.js, NextAuth.js - отличное решение, чтобы не заморачиваться с реализацией сложной безопасности самостоятельно. Он имеет обширный список провайдеров для быстрого добавления аутентификации OAuth и предоставляет адаптеры для многих баз данных и ORM.
Провайдер контекста
В точке входа вашего приложения вы увидите, что ваше приложение обернуто в SessionProvider↗:
<SessionProvider session={session}>
<Component {...pageProps} />
</SessionProvider>
Этот провайдер контекста позволяет вашему приложению получить доступ к данным сессии из любого места вашего приложения, не передавая их как пропсы:
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
:
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
.
callbacks: {
session({ session, user }) {
if (session.user) {
session.user.id = user.id;
}
return session;
},
},
Это связано с файлом объявления типов, чтобы убедиться, что user.id
типизирован при доступе к объекту session
. Подробнее о Module Augmentation
↗ в документации NextAuth.js.
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
настраивает все это для вас, позволяя вам легко получать доступ к объекту сессии в аутентифицированных процедурах.
Это делается в два шага:
- Возьмите сессию из заголовков запроса с помощью функции
getServerSession
↗. Преимущество использованияgetServerSession
вместо обычногоgetSession
заключается в том, что это server-side функция и она не вызывает ненужных вызовов fetch.create-t3-app
создает вспомогательную функцию, которая абстрагирует этот особый API.
export const getServerAuthSession = async (ctx: {
req: GetServerSidePropsContext["req"];
res: GetServerSidePropsContext["res"];
}) => {
return await getServerSession(ctx.req, ctx.res, authOptions);
};
Используя эту вспомогательную функцию, мы можем получить сессию и передать ее в контекст tRPC:
import { getServerAuthSession } from "../auth";
export const createContext = async (opts: CreateNextContextOptions) => {
const { req, res } = opts;
const session = await getServerAuthSession({ req, res });
return await createContextInner({
session,
});
};
- Создайте tRPC middleware, которое проверяет, аутентифицирован ли пользователь. Затем мы используем middleware в
protectedProcedure
. Любой вызывающий эти процедуры должен быть аутентифицирован, иначе будет сгенерирована ошибка, которую можно правильно обработать на стороне клиента.
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
у вас есть доступ к идентификатору пользователя, который можно использовать для получения большего количества данных из базы данных.
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
:
+ 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 по умолчанию
- Перейдите в раздел Applications в Discord Developer Portal↗, и нажмите на “New Application”
- В меню настроек перейдите к “OAuth2 => General”
- Скопируйте Client ID и вставьте его в
AUTH_DISCORD_ID
в.env
. - Возле Client Secret нажмите “Reset Secret” и скопируйте эту строку в
AUTH_DISCORD_SECRET
в.env
. Будьте осторожны, поскольку вы больше не сможете увидеть этот секрет, и сброс его приведет к тому, что существующий истечет. - Нажмите “Add Redirect” и вставьте
<app url>/api/auth/callback/discord
(пример для локальной разработки:http://localhost:3000/api/auth/callback/discord↗
) - Сохраните изменения
- Возможно, но не рекомендуется, использовать одно и то же приложение Discord для разработки и продакшена. Вы также можете рассмотреть Mocking the Provider↗ во время разработки.
Полезные ресурсы
Ресурс | Ссылка |
---|---|
Документация NextAuth.js | https://next-auth.js.org/↗ |
NextAuth.js GitHub | https://github.com/nextauthjs/next-auth↗ |
tRPC Kitchen Sink - with NextAuth | https://kitchen-sink.trpc.io/next-auth↗ |