Jump to content

NextAuth.js

Quando voc√™ deseja um sistema de autentica√ß√£o em sua aplica√ß√£o Next.js, o NextAuth.js √© uma excelente solu√ß√£o para trazer a complexidade da seguran√ßa sem o inc√īmodo de ter que constru√≠-lo sozinho. Ele vem com uma extensa lista de provedores para adicionar rapidamente a autentica√ß√£o OAuth e fornece adaptadores para muitos bancos de dados e ORMs.

Context Provider

No ponto de entrada do seu aplicativo, você verá que ele está envolvida pelo SessionProvider↗:

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

Este provedor de contexto permite que seu aplicativo acesse os dados da sess√£o de qualquer lugar em seu aplicativo, sem ter que pass√°-los como props:

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

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

  if (!session) {
    // Lida com o estado n√£o autenticado, por exemplo renderizar um componente SignIn
    return <SignIn />;
  }

  return <p>Bem-vindo {session.user.name}!</p>;
};

Inclus√£o do user.id na Sess√£o

create-t3-app está configurado para utilizar o retorno de chamada de sessão (sesion callback)↗ na configuração NextAuth.js para incluir o ID do usuário dentro do objeto session.

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

Isso é acoplado a um arquivo de declaração de tipo para garantir que o user.id tenha seu tipo quando acessado no objeto session. Leia mais sobre Module Augmentation↗ nos documentos de NextAuth.js.

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

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

O mesmo padr√£o pode ser usado para adicionar quaisquer outros dados ao objeto session, como um campo role, mas n√£o deve ser mal utilizado para armazenar dados confidenciais no cliente.

Uso com tRPC

Ao usar NextAuth.js com tRPC, você pode criar procedimentos protegidos e reutilizáveis usando middleware↗. Isso permite criar procedimentos que só podem ser acessados por usuários autenticados. create-t3-app configura tudo isso para você, permitindo que você acesse facilmente o objeto de sessão dentro de procedimentos autenticados.

Isso é feito em um processo de duas etapas:

  1. Pegar a sessão dos cabeçalhos de solicitação usando a função getServerSession↗. Não se preocupe, esta função é segura de usar - o nome inclui unstable apenas porque a implementação da API pode mudar no futuro. A vantagem de usar getServerSession em vez do getSession regular é que é uma função somente do lado do servidor e não aciona chamadas de busca desnecessárias. create-t3-app cria uma função auxiliar que abstrai essa API peculiar.
server/common/get-server-auth-session.ts
export const getServerAuthSession = async (ctx: {
  req: GetServerSidePropsContext["req"];
  res: GetServerSidePropsContext["res"];
}) => {
  return await getServerSession(ctx.req, ctx.res, nextAuthOptions);
};

Usando esta função auxiliar, podemos pegar a sessão e passá-la para o contexto tRPC:

server/trpc/context.ts
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,
  });
};
  1. Criar um middleware tRPC que verifique se o usuário está autenticado. Em seguida, usamos o middleware em um protectedProcedure. Qualquer chamador para esses procedimentos deve ser autenticado, caso contrário, será lançado um erro que pode ser tratado adequadamente pelo cliente.
server/trpc/trpc.ts
const isAuthed = t.middleware(({ ctx, next }) => {
  if (!ctx.session || !ctx.session.user) {
    throw new TRPCError({ code: "UNAUTHORIZED" });
  }
  return next({
    ctx: {
      // Infere `session` como n√£o-nulo
      session: { ...ctx.session, user: ctx.session.user },
    },
  });
});

export const protectedProcedure = t.procedure.use(isAuthed);

O objeto de sessão é uma representação leve e mínima do usuário e contém apenas alguns campos. Ao usar protectedProcedures, você tem acesso ao id do usuário que pode ser usado para buscar mais dados do banco de dados.

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

Uso com Prisma

Fazer o NextAuth.js funcionar com o Prisma requer muita configuração inicial↗. O create-t3-app lida com tudo isso para você e, se você selecionar Prisma e NextAuth.js, obterá um sistema de autenticação totalmente funcional com todos os modelos necessários pré-configurados. Enviamos seu aplicativo montado com um provedor do Discord OAuth pré-configurado, que escolhemos porque é um dos mais fáceis de começar - basta fornecer seus tokens no .env e pronto. No entanto, você pode adicionar facilmente mais provedores seguindo a documentação do NextAuth.js↗. Observe que certos provedores exigem que campos extras sejam adicionados a determinados modelos. Recomendamos que você leia a documentação do provedor que deseja usar para certificar-se de que possui todos os campos obrigatórios.

Adicionando novos campos aos seus modelos

Ao adicionar novos campos a qualquer um dos modelos User, Account, Session ou VerificationToken (provavelmente você só precisaria modificar o modelo User), você precisa ter em mente que o Adaptador Prisma↗ cria campos automaticamente nesses modelos quando novos usuários se inscrevem e fazem login. Portanto, ao adicionar novos campos a esses modelos, você deve fornecer padrão valores para eles, pois o adaptador não está ciente desses campos.

Se, por exemplo, você quiser adicionar um role (cargo) ao modelo User, você precisará fornecer um valor padrão para o campo role. Isso é feito adicionando um valor @default ao campo role no modelo User:

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

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

Uso com o middleware Next.js

Uso de NextAuth.js com middleware Next.js requer o uso da estratégia de sessão JWT↗ para autenticação. Isso ocorre porque o middleware só consegue acessar o cookie da sessão se for um JWT. Por padrão, create-t3-app é configurado para usar a estratégia de banco de dados padrão, em combinação com o Prisma como o adaptador de banco de dados.

Configurando o DiscordProvider padr√£o

  1. V√° para a se√ß√£o Aplicativos no Portal do desenvolvedor do Discord‚Üó e clique em ‚ÄúNovo aplicativo‚ÄĚ
  2. No menu de configura√ß√Ķes, v√° para ‚ÄúOAuth2 => Geral‚ÄĚ
  • Copie o Client ID e cole-o em DISCORD_CLIENT_ID em .env.
  • Em Client Secret, clique em ‚ÄúReset Secret‚ÄĚ e copie essa string para DISCORD_CLIENT_SECRET em .env. Tenha cuidado, pois voc√™ n√£o poder√° ver esse segredo novamente e redefini-lo far√° com que o existente expire.
  • Clique em ‚ÄúAdd Redirect‚ÄĚ e cole em <app url>/api/auth/callback/discord (exemplo para desenvolvimento local: http://localhost:3000/api/auth/callback/discord‚Üó)
  • Salve suas altera√ß√Ķes
  • √Č poss√≠vel, mas n√£o recomendado, usar o mesmo aplicativo Discord tanto para desenvolvimento quanto para produ√ß√£o. Voc√™ tamb√©m pode considerar mockar o Provider‚Üó durante o desenvolvimento.

Recursos √öteis

RecursoLink
Documentação do NextAuth.jshttps://next-auth.js.org/↗
GitHub do NextAuth.jshttps://github.com/nextauthjs/next-auth‚Üó
tRPC Kitchen Sink - com NextAuthhttps://kitchen-sink.trpc.io/next-auth‚Üó

Recent Contributors To This Page