NextAuth.js
Kiedy chcesz dodać system kont do swojej aplikacji Next.js, NextAuth.js to znakomite rozwiązanie. Pozwala ono wdrożyć złożone systemy bezpieczeństwa nie zmuszając Cię przy tym do pisania ich własnoręcznie. NextAuth.js zawiera rozległą listę providerów, które zapewnią Ci szybki sposób na dodanie OAutha. Paczka ta posiada również wiele adapterów dla baz danych i ORMów.
Context Provider
W pliku pages/_app.tsx
zobaczyć możesz, iż twoja aplikacja znajduje się w SessionProviderze↗:
<SessionProvider session={session}>
<Component {...pageProps} />
</SessionProvider>
Ten oto provider kontekstu pozwala twojej aplikacji na dostęp do danych sesji z każdego miejsca, bez potrzeby przesyłania ich po “propsach”:
import { useSession } from "next-auth/react";
const User = () => {
const { data: session } = useSession();
if (!session) {
// Obsłuż status nie bycia zalogowanym, np. wyświetl komponent `SignIn`
return <SignIn />;
}
return <p>Welcome {session.user.name}!</p>;
};
Otrzymywanie sesji po stronie serwera
Czasem możesz chcieć otrzymać sesję na serwerze. Aby to zrobić, pobierz sesję korzystając z funkcji pomocniczej getServerAuthSession
dostarczanej przez Create T3 App a następnie prześlij ją do klienta korzystając z 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();
// UWAGA: obiekt `session` nie będzie miał stanu ładowania, ponieważ jest już pobrany na serwerze
...
}
Dołączanie user.id
do sesji
Create T3 App jest skonfigurowany tak, aby wykorzystać callback session
↗ w konfiguracji NextAuth.js do dodania ID użytkownika do obiektu session
.
callbacks: {
session({ session, user }) {
if (session.user) {
session.user.id = user.id;
}
return session;
},
},
Łączy się to z plikiem deklaracji typów, aby zapewnić odpowiednie typy obiektu session
- musi on zawierać powyższe pole user.id
. Więcej o zmianie typów poczytać możesz w rozdziale Module Augmentation
↗ dokumentacji NextAuth.js.
import { DefaultSession } from "next-auth";
declare module "next-auth" {
interface Session {
user?: {
id: string;
} & DefaultSession["user"];
}
}
Ten sam sposób wykorzystany może zostać do dodania większej ilości danych na obiekcie session
, takich jak pole role
(rola). Nie powinien być on jednak wykorzystany do zapisywania wrażliwych danych dla klienta.
Korzystanie wraz z tRPC
Jeżeli używasz NextAuth.js oraz tRPC, stworzyć można zabezpieczone procedury (z możliwością wielokrotnego użycia) korzystając z middleware’ów↗. Pozwala to na umieszczenie procedur, które zainicjowane być mogą jedynie przez autoryzowanych użytkowników. create-t3-app
wszystko to dla Ciebie konfiguruje, dając ci możliwość łatwego dostępu do obiektu sesji wśród autoryzowanych procedur.
Konfiguracja ta zachodzi w dwóch krokach:
- Pobierz sesję z headerów zapytania korzystając z funkcji
getServerSession
↗. Zaletą korzystania zgetServerSession
zamiastgetSession
jest fakt, iż jest to funkcja wywoływana jedynie po stronie serwera i nie inicjuje ona żadnych niepotrzebnych zapytań.create-t3-app
tworzy funkcję pomocniczą, która ułatwia korzystanie zgetServerSession
- nie musisz więc importować zarówno opcji NextAuth.js jak i funkcjigetServerSession
za każdym razem, kiedy chcesz otrzymać sesję.
export const getServerAuthSession = (ctx: {
req: GetServerSidePropsContext["req"];
res: GetServerSidePropsContext["res"];
}) => {
return getServerSession(ctx.req, ctx.res, authOptions);
};
Korzystając z tej funkcji pomocniczej, możemy otrzymać sesję i przesłać ją do kontekstu tRPC:
import { getServerAuthSession } from "../common/get-server-auth-session";
export const createContext = async (opts: CreateNextContextOptions) => {
const { req, res } = opts;
const session = await getServerAuthSession({ req, res });
return await createContextInner({
session,
});
};
- Stwórz middleware tRPC, który sprawdza, czy użytkownik jest autoryzowany. Wykorzystamy następnie stworzony middleware w
protectedProcedure
- specjalnej, zabezpieczonej procedurze. Każda osoba wywołująca ją będzie musiała spełniać warunki autoryzacji - w przeciwnym razie wystąpi błąd, który w odpowiedni sposób będzie mógł zostać obsłużony po stronie klienta.
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.session || !ctx.session.user) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({
ctx: {
// inferer `session` som ikke-nullbar
session: { ...ctx.session, user: ctx.session.user },
},
});
}));
Obiekt session
to minimalna i lekka reprezentacja użytkownika zawierająca jedynie parę pól. Jeśli korzystasz z procedur protectedProcedure
, masz dostęp do ID użytkownika, które może zostać wykorzystane do pobrania większej ilości danych z bazy.
const userRouter = router({
me: protectedProcedure.query(async ({ ctx }) => {
const user = await prisma.user.findUnique({
where: {
id: ctx.session.user.id,
},
});
return user;
}),
});
Korzystanie wraz z Prismą
Przygotowanie NextAuth.js do pracy z Prismą wymaga wiele wstępnej konfiguracji↗. create-t3-app
zajmuje się nią za Ciebie, a jeśli wybierzesz zarówno Prismę i NextAuth.js, otrzymasz w pełni działający system uwierzytelniania, wraz z wszystkimi potrzebnymi modelami bazy danych. Szablon aplikacji, który dostarczamy, zawiera domyślnie provider OAuth Discorda, który wybraliśmy z powodu bycia jednym z łatwiejszych do obsługiwania - jedyne, co musiz zrobić, to podać tokeny w pliku .env
… i gotowe! Jeżeli jednak chcesz skorzystać z czegoś innego, możesz w łatwy sposób dołączyć inne providery opierając się na dokumentacji NextAuth.js↗. Uwaga - niektóre z providerów wymagają większej ilości pól na modelach w bazie danych. Polecamy Ci przeczytać dokumentację providera, z którego chciałbyś skorzystać aby upewnić się, iż posiadasz wszystko, czego potrzebujesz.
Dodawanie nowych pól do modeli
Kiedy dodajesz nowe pola do któregokolwiek z modeli - User
, Account
, Session
lub VerificationToken
(prawdopodobnie będziesz chciał zmieniać jedynie model User
) - pamiętać musisz, iż adapter dla Prismy↗ automatycznie tworzy pola na powyższych modelach w przypadku rejestracji kont i logowania na nie. Z tego też powodu, dodając nowe pola do tychże modeli, zaopatrzyć musisz je w wartości domyślne - adapter nie jest ich świadomy.
Jeżeli przykładowo chcesz dodać pole role
(roli) do modelu User
(użytkownika), musisz pamiętać o dołączeniu wartości domyślnej tego pola. Zrobisz to za pomocą wartości @default
w modelu User
:
+ enum Role {
+ USER
+ ADMIN
+ }
model User {
...
+ role Role @default(USER)
}
Korzystanie wraz z middlewarem Next.js
Wykorzystanie middleware’a Next.js wymaga od Ciebie skorzystania ze strategii JWT↗ do autoryzacji. Dzieje się tak, ponieważ middleware jest w stanie pobierać ciasteczko sesji jedynie, gdy jest ono JWT. Create T3 App jest skonfigurowany tak, by wykorzystać domyślną strategię bazy danych, wraz z Prismą jako jej adapterem.
Konfigurowanie domyślnego providera Discord (DiscordProvider
)
- Przejdź do sekcji Aplikacje w Panelu Discord Developer Portal↗, a następnie kliknij na “New Application”
- W menu ustawień, przejdź do “OAuth2 => General”
- Skopiuj Client ID i wklej go do pliku
.env
pod kluczemAUTH_DISCORD_ID
. - Pod Client Secret, kliknij “Reset Secret” i skopiuj podany tekst do pliku
.env
pod kluczemAUTH_DISCORD_SECRET
. Uważaj - nie będziesz mógł ponownie zobaczyć tego klucza, a jego reset spowoduje wygaśnięcie aktualnego. - Dodaj “Add Redirect” i wklej tam
<app url>/api/auth/callback/discord
(przykładowo dla lokalnej aplikacji:http://localhost:3000/api/auth/callback/discord↗
) - Zapisz zmiany
- Jest możliwość (nie jest ona jednak polecana), aby wykorzystać tą samą aplikację Discorda dla zarówno aplikacji lokalnej i tej w wersji produkcyjnej. Możesz także wykorzystać mockowanie providera↗ podczas rozwoju aplikacji.
Przydatne Zasoby
Zasób | Link |
---|---|
Dokumentacja NextAuth.js | https://next-auth.js.org/↗ |
GitHub NextAuth.js | https://github.com/nextauthjs/next-auth↗ |
tRPC Kitchen Sink - wraz z NextAuth | https://kitchen-sink.trpc.io/next-auth↗ |