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↗:
<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:
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
:
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
.
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.
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:
- Pegar a sessão dos headers da request usando a função
getServerSession
↗. A vantagem de usargetServerSession
em vez dogetSession
regular é que é uma função que é executada somente do lado do servidor e não faz chamadas de busca desnecessárias. Ocreate-t3-app
cria uma função auxiliar que abstrai essa API peculiar.
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:
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,
});
};
- 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.
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.
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
:
+ 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
- Vá para a seção Aplicativos no Portal do desenvolvedor do Discord↗ e clique em “Novo aplicativo”
- 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
Recurso | Link |
---|---|
Documentação do NextAuth.js | https://next-auth.js.org/↗ |
GitHub do NextAuth.js | https://github.com/nextauthjs/next-auth↗ |
tRPC Kitchen Sink - com NextAuth | https://kitchen-sink.trpc.io/next-auth↗ |