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

Recuperando a sessão do lado do servidor.

Às vezes, você poderá querer solicitar a sessão no servidor. Para fazer isso, faça uma requisição usando o utilitário getServerAuthSession que create-t3-app fornece e passe-a para o cliente usando 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();
  // NOTA: `session` não terá um estado de carregamento, pois já foi pré-carregado no servidor

  ...
}

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 headers da request usando a função getServerSession. A vantagem de usar getServerSession em vez do getSession regular é que é uma função que é executada somente do lado do servidor e não faz chamadas de busca desnecessárias. O 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
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 },
    },
  });
}));

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 AUTH_DISCORD_ID em .env.
  • Em Client Secret, clique em “Reset Secret” e copie essa string para AUTH_DISCORD_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