Jump to content

NextAuth.js

Hvis du vil ha et autentiseringssystem i Next.js-applikasjonen din, er NextAuth.js en utmerket lÞsning for Ä unngÄ kompleksiteten med Ä bygge det selv. Den kommer med en omfattende liste leverandÞrer for raskt Ä legge til OAuth-autentisering og tilbyr adaptere for mange databaser og ORM-er.

KontekstleverandĂžr

I applikasjonens inngangspunkt vil du se at applikasjonen er pakket inn av en SessionProvider↗.

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

Denne kontekstleverandÞren lar applikasjonen din fÄ tilgang til din session-data fra hvor som helst i applikasjonen din uten Ä mÄtte sende dem som props:

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

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

  if (!session) {
    // HÄndter uautentisert state f.eks. ved Ä vise en pÄloggingskomponent
    return <SignIn />;
  }

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

Henting av session pÄ serversiden

Noen ganger vil du kanskje be om session pĂ„ serveren. For Ă„ gjĂžre dette, prefetch’er du session ved Ă„ bruke getServerAuthSession-hjelperfunksjonen som create-t3-app gir, sender den videre til klienten ved Ă„ bruke 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();
  // MERK: `session` vil ikke ha en lastestatus siden den allerede er prefetched pÄ serveren
  ...
}

Inkluder user.id i din Session

create-t3-app er konfigurert til Ă„ bruke session callback↗ i NextAuth.js-konfigurasjonen for Ă„ inkludere bruker-ID i ‘session’-objektet.

server/auth.ts
callbacks: {
    session({ session, user }) {
      if (session.user) {
        session.user.id = user.id;
      }
      return session;
    },
  },

Dette er kombinert med en typedeklarasjonsfil for Ă„ sikre at user.id er riktig typet nĂ„r du fĂ„r tilgang til session-objektet. Les mer om "Module Augmentation"↗ i NextAuth.js-dokumentasjonen.

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

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

Det samme mÞnsteret kan brukes til Ä legge til flere data til session-objektet, som f.eks et role-felt, men skal ikke misbrukes til Ä lagre sensitive data pÄ klienten.

Bruk med tRPC

Hvis du bruker NextAuth.js med tRPC, kan du opprette gjenbrukbare beskyttede prosedyrer med middlewares↗. Dette lar deg lage prosedyrer som bare er tilgjengelige for autentiserte brukere. create-t3-app gir deg allerede dette, slik at du enkelt kan fĂ„ tilgang til session-objektet i autentiserte prosedyrer.

Dette skjer i to trinn:

  1. FĂ„ tilgang til sesjonen fra request-headerne ved Ă„ bruke funksjonen getServerSession↗. Fordelen med getServerSession sammenlignet med getSession er at det er en funksjon pĂ„ serversiden og medfĂžrer ikke unĂždvendige kall. create-t3-app lager en hjelpefunksjon som abstraherer dette sĂŠregne API-et, slik at du ikke trenger Ă„ importere bĂ„de NextAuth.js-alternativene dine sĂ„ vel som getServerSession-funksjonen hver gang du trenger tilgang til sesssion.
server/auth.ts
export const getServerAuthSession = (ctx: {
  req: GetServerSidePropsContext["req"];
  res: GetServerSidePropsContext["res"];
}) => {
  return getServerSession(ctx.req, ctx.res, authOptions);
};

Med denne hjelpefunksjonen kan vi hente sesjonen og sende den videre til tRPC-konteksten:

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. Lag en tRPC-middleware som sjekker om brukeren er autentisert. Vi bruker deretter middlewaren i en protectedProcedure. Hvert kall av disse prosedyrene mÄ autentiseres, ellers kastes en feilmelding, som kan hÄndteres av klienten.
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: {
      // inferer `session` som ikke-nullbar
      session: { ...ctx.session, user: ctx.session.user },
    },
  });
}));

Session-objektet er en minimal representasjon av brukeren og inneholder bare noen fÄ felt. Hvis du bruker protectedProcedures, har du tilgang til brukerens ID, som kan brukes til Ä hente ut mer data fra databasen.

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;
  }),
});

Bruk med Prisma

Mye fĂžrstegangsoppsett↗ kreves for Ă„ bruke NextAuth.js med Prisma. create-t3-app vil gjĂžre dette for deg, og hvis du velger bĂ„de Prisma og NextAuth.js, fĂ„r du et fullt funksjonelt autentiseringssystem med alle nĂždvendige modeller forhĂ„ndskonfigurert. Vi oppretter applikasjonen din med en forhĂ„ndskonfigurert Discord OAuth-leverandĂžr, som vi valgte siden den er en av de enkleste leverandĂžrene Ă„ komme i gang med – du trenger bare Ă„ legge inn tokens i .env-filen og du er i gang. Du kan imidlertid enkelt legge til flere leverandĂžrer ved Ă„ fĂžlge NextAuth.js-dokumentasjonen↗. VĂŠr oppmerksom pĂ„ at enkelte leverandĂžrer krever at du legger til flere felt i enkelte modeller. Vi anbefaler Ă„ lese dokumentasjonen for leverandĂžren du planlegger Ă„ bruke for Ă„ sikre at alle obligatoriske felt er til stede.

Legge til nye felt i modellene dine

Hvis du legger til nye felt i noen av modellene User, Account, Session eller VerificationToken (du trenger sannsynligvis bare Ă„ justere User-modellen), mĂ„ du huske pĂ„ at Prisma-adapteren↗ automatisk legger til felt i disse modellene nĂ„r nye brukere registrerer seg og logger pĂ„. SĂ„ nĂ„r du legger til nye felt i disse modellene, mĂ„ du oppgi standardverdier for dem fordi adapteren ikke vet om disse feltene.

Hvis du for eksempel vil legge til et role-felt i User-modellen, mÄ du angi en standardverdi for feltet. Dette oppnÄs ved Ä legge til en @default-verdi i role-feltet i User-modellen:

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

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

Bruk med Next.js middleware

Bruk av NextAuth.js med Next.js middleware krever bruk av “JWT session strategy”↗ for autentisering. Dette er fordi middlewaren bare kan fĂ„ tilgang til session-informasjonskapslene nĂ„r den er en JWT. Som standard er create-t3-app konfigurert til Ă„ bruke default-databasestrategien, i kombinasjon med Prisma som databaseadapter.

Oppsett av DiscordProvider (standard)

  1. Naviger til Applications-delen i Discord Developer Portal↗ og klikk pĂ„ “New Application”

  2. Bytt til “OAuth2 => Generelt” i settings-menyen

  • Kopier klient-ID-en og lim den inn i DISCORD_CLIENT_ID i .env.
  • Under Client Secret, klikk pĂ„ “Reset Secret” og kopier denne strengen til DISCORD_CLIENT_SECRET i .env. VĂŠr forsiktig siden du ikke lenger vil kunne se denne hemmeligheten og tilbakestilling av den vil fĂžre til at den eksisterende hemmeligheten utlĂžper.
  • Klikk pĂ„ “Add Redirect” og lim inn <app url>/api/auth/callback/discord (eksempel for utvikling i lokal miljĂž: http://localhost:3000/api/auth/callback/discord↗)
  • Lagre endringene dine
  • Det er mulig, men ikke anbefalt, Ă„ bruke samme Discord-applikasjon for utvikling og produksjon. Du kan ogsĂ„ vurdere Ă„ Mocke leverandĂžren↗ under utviklingen.

Nyttige Ressurser

RessurserLink
NextAuth.js Dokumentasjonhttps://next-auth.js.org/↗
NextAuth.js GitHubhttps://github.com/nextauthjs/next-auth↗
tRPC Kitchen Sink - med NextAuthhttps://kitchen-sink.trpc.io/next-auth↗