Next.js: O Guia Complete para Autenticação
Tags: ptbr, react, aws, javascript
Published: 2020-09-24 22:24:33

> Créditos da Imagem por [Kai Pilger](https://unsplash.com/@kaip)

---

# Créditos

- [The Complete Guide to Next.js Authentication](https://dev.to/dabit3/the-complete-guide-to-next-js-authentication-2aco), escrito originalmente por [Nader Dabit](https://dev.to/dabit3).

---

Neste guia, você aprenderá como implementar a autenticação em um aplicativo Next.js. Abordarei autenticação de cliente, páginas autenticadas renderizadas no servidor, rotas de API autenticadas, rotas protegidas e redirecionamentos.

O serviço de autenticação será implementado com [AWS Amplify](https://docs.amplify.aws/), mas as ideias e estratégias abordadas aqui funcionarão para qualquer serviço de autenticação como [Auth0](https://auth0.com/) / [Okta](https://www.okta.com/) ou mesmo uma implementação de back-end personalizada, desde que forneça uma maneira de gerenciar sessões no cliente e servidor.

> O código para este projeto está localizado [aqui](https://github.com/dabit3/next.js-authentication-aws) . O vídeo passo a passo está [aqui](https://dev.to/dabit3/the-complete-guide-to-next-js-authentication-2aco#video-walkthrough).

## Visão geral do Next.js

Next.js combina a renderização do lado do cliente com HTML pré-renderizado na forma de páginas estáticas e renderizadas pelo servidor. O framework também deixa muito fácil a criação de APIs com rotas de API.

Ao executar uma compilação, o framework determinará se uma página deve ser gerada estaticamente ou se deve ser renderizada pelo servidor. Por padrão, todas as páginas são geradas estaticamente, a menos que a página esteja usando a função `getServerSideProps` para passar `props` para a página. Além disso, todas as rotas de API serão, por padrão, renderizadas pelo servidor.

## Conceitos de autenticação Next.js

Ao trabalhar em um aplicativo Next.js, você normalmente quer tirar proveito de todos esses recursos e fazer com que suas APIs funcionem perfeitamente com o framework (cliente e servidor). O problema é que, geralmente, não é fácil acessar com segurança a sessão do usuário no cliente e no servidor.

Neste guia, mostrarei como habilitar a autenticação e autorização do usuário para implementar o seguinte:

1.  Autenticação de cliente
2.  Acessando a sessão do usuário no cliente
3.  Rotas de cliente protegidas
4.  Redirecionamentos do lado do cliente
5.  Acessando a sessão do usuário em uma rota do lado do servidor ( `getServerSideProps`)
6.  Rotas de servidor protegidas ( `getServerSideProps`)
7.  Redirecionamentos do lado do servidor ( `getServerSideProps`)
8.  Acessando a sessão do usuário em uma rota de API
9.  Login social (OAuth)
10.  Implantar o aplicativo usando o componente Next.js Serverless

## Começando

Para começar, primeiro crie um novo aplicativo Next.js:  

```bash
npx create-next-app next-authentication
```

Em seguida, mude para o novo diretório e instale as dependências:  

```bash
cd next-authentication
npm install aws-amplify @aws-amplify/ui-react emotion
```

Inicialize um novo projeto Amplify:  

```bash
amplify init

> Choose defaults when prompted
```

> Se você ainda não instalou e configurou o Amplify CLI, veja [este vídeo](https://www.youtube.com/watch?v=n4DuYTzpvdE) para uma explicação completa.

Adicione o serviço de autenticação:  

```bash
amplify add auth

? Do you want to use the default authentication and security configuration? Default configuration
? How do you want users to be able to sign in? Username
? Do you want to configure advanced settings? No, I am done.
```

Em seguida, implante o serviço de autenticação:  

```bash
amplify push --y
```

### Ativando Amplify SSR

Para ativar o suporte ao Amplify SSR, abra **pages/_app.js** e adicione o seguinte na parte superior do arquivo:  

```jsx
import Amplify from 'aws-amplify'
import config from '../src/aws-exports'
Amplify.configure({
  ...config,
  ssr: true
})
```

🔥 Definindo `ssr`como `true` é tudo o que você precisa fazer para tornar seu aplicativo Amplify compatível com SSR.

## Criação da rota de autenticação / perfil

Em seguida, crie um novo arquivo no diretório de **pages** denominado **profile.js** .

Aqui, vamos habilitar a autenticação usando o componente `withAuthenticator`. Este componente criará um fluxo de autenticação do usuário, permitindo que um usuário se inscreva com MFA e faça login.

Neste arquivo, adicione o seguinte código:  

```jsx
// pages/profile.js
import { useState, useEffect } from 'react'
import { Auth } from 'aws-amplify'
import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react'

function Profile() {
  const [user, setUser] = useState(null)
  useEffect(() => {
    // Acessa a sessão do usuário no cliente
    Auth.currentAuthenticatedUser()
      .then(user => {
        console.log("User: ", user)
        setUser(user)
      })
      .catch(err => setUser(null))
  }, [])
  return (
    <div>
      { user && <h1>Welcome, {user.username}</h1> }
      <AmplifySignOut />
    </div>
  )
}

export default withAuthenticator(Profile)

```

Por fim, atualize **pages/_app.js** para adicionar um pouco de navegação entre as páginas:  

```jsx
import '../styles/globals.css'
import Link from 'next/link'
import { css } from 'emotion'

import Amplify from 'aws-amplify'
import config from '../src/aws-exports'
Amplify.configure({
  ...config,
  ssr: true
})

export default function MyApp({ Component, pageProps }) {
  return (
    <div>
      <nav className={navStyle}>
        <Link href="/">
          <span className={linkStyle}>Home</span>
        </Link>
        <Link href="/profile">
          <span className={linkStyle}>Profile</span>
        </Link>
      </nav>
      <Component {...pageProps} />
    </div>
  )
}

const linkStyle = css`
  margin-right: 20px;
  cursor: pointer;
`

const navStyle = css`
  display: flex;
`
```

### Opcional - Estilo do componente

Você pode configurar o estilo do componente de autenticação. Por exemplo, para tentar combinar o esquema de cor azul que acompanha o projeto inicial de Next.js, você pode adicionar o seguinte na parte inferior de **styles/globals.css** :  

```css
:root {
  --amplify-primary-color: #0083e8;
  --amplify-primary-tint: #006ec2;
  --amplify-primary-shade: #006ec2;
}
```

### Criar uma conta e fazer login

Agora que a rota de perfil foi criada, vamos testá-la criando uma nova conta e fazendo login.  

```bash
npm run dev
```

> Clique [aqui](https://docs.amplify.aws/ui/auth/authenticator/q/framework/react) para obter mais maneiras de personalizar o componente `withAuthenticator`.

Você deve conseguir navegar para a rota **/profile** para criar uma conta e fazer login.

### Usando a classe Auth diretamente

Se você quer construir seu próprio fluxo de autenticação personalizada, você também pode aproveitar a [classe Auth](https://docs.amplify.aws/lib/auth/emailpassword/q/platform/js) que tem mais de 30 métodos para gerenciar o estado da autenticação do usuário, incluindo métodos, como `signUp`, `confirmSignUp`, `signIn`, e `forgotPassword`.

## Acessando a sessão do usuário em uma rota SSR

Agora que os usuários podem fazer o login, vamos criar uma nova rota para testar o SSR.

Crie uma nova rota chamada **/protected.js** no diretório de páginas.

Aqui, queremos ter uma rota que autentique o usuário no servidor e retorne uma mensagem de sucesso ou de erro com base no estado de autenticação do usuário. 

```jsx
// pages/protected.js

import { withSSRContext } from 'aws-amplify'

function Protected({ authenticated, username }) {
  if (!authenticated) {
    return <h1>Not authenticated</h1>
  }
  return <h1>Hello {username} from SSR route!</h1>
}

export async function getServerSideProps(context) {
  const { Auth } = withSSRContext(context)
  try {
    const user = await Auth.currentAuthenticatedUser()
    console.log('user: ', user)
    return {
      props: {
        authenticated: true, username: user.username
      }
    }
  } catch (err) {
    return {
      props: {
        authenticated: false
      }
    }
  }
}

export default Protected
```

Em seguida, atualize a **navegação** em **pages/_app.js** com um link para a nova rota:  

```jsx
<Link href="/protected">
  <span className={linkStyle}>Protected route</span>
</Link>
```

Agora, quando você estiver conectado, perceberá que poderá acessar o usuário autenticado no método `getServerSideProps`. Você também deve ver o objeto de usuário printado no terminal.

Isso é feito usando a função `withSSRContext` da biblioteca `aws-amplify` que retorna o objeto `Auth` e com isso, chamamos o `Auth.currentAuthenticatedUser()`. Ao obter acesso à classe `Auth` dessa maneira, o Amplify lerá automaticamente o objeto de solicitação da requisição (`context`) e dará a você acesso à sessão do usuário conectado em ambas as rotas de API e também em rotas SSR.

## Acessando a sessão do usuário em uma rota de API

Nesta rota de API, queremos acessar a sessão do usuário e retornar `null` para um usuário que não está autenticado ou o nome de usuário para um usuário que está autenticado.

Para fazer isso, crie um novo arquivo em **pages/api** chamado **check-user.js** :  

```jsx
// pages/api/check-user.js
import Amplify, { withSSRContext } from 'aws-amplify'
import config from "../../src/aws-exports.js"

// Configuração necessária para habilitar o Amplify SSR em rotas de API
Amplify.configure({ ...config, ssr: true })

export default async (req, res) => {
  const { Auth } = withSSRContext({ req })
  try {
    const user = await Auth.currentAuthenticatedUser()
    res.json({ user: user.username })
  } catch (err) {
    res.statusCode = 200
    res.json({ user: null })
  }
}
```

Ao navegar ou tentar acessar **/api/check-user,** você perceberá que o objeto de usuário está disponível quando você está autenticado e indisponível quando você não está autenticado.

## Redirecionamento do lado do cliente

Freqüentemente, você irá detectar se um usuário está conectado e permitir o acesso ou redirecioná-lo com base na sessão ou suas credenciais.

Para fazer isso, você pode usar o gancho `withRouther` do Next.js, para navegar programaticamente com o usuário com base no estado de sua sessão.

Crie um novo arquivo no diretório **pages** chamado **protected-client-route.js**.

Aqui, adicione o seguinte código:  

```jsx
import { useState, useEffect } from 'react'
import { Auth } from 'aws-amplify'
import { useRouter } from 'next/router'

function ProtectedClient() {
  const [user, setUser] = useState(null)
  const router = useRouter()
  useEffect(() => {
    Auth.currentAuthenticatedUser()
      .then(user => setUser(user))
      // Se o usuário não estiver autenticado, redirecione ele para a página `/profile`
      .catch(() => router.push('/profile'))
  }, [])
  if (!user) return null
  return <h1>Hello {user.username} from client route!</h1>
}

export default ProtectedClient
```

Em seguida, adicione um link para esta rota em **pages/_app.js** :  

```jsx
<Link href="/protected-client-route">
  <span className={linkStyle}>Protected client route</span>
</Link>
```

Se você tentar acessar a rota do cliente protegida, será redirecionado automaticamente para a rota do **/profile** se não estiver autenticado e se estiver autenticado, terá permissão para ver a página!

## Redirecionamentos do lado do servidor

Um dos benefícios do SSR é a capacidade de implementar redirecionamentos do lado do servidor. Usar um redirecionamento do lado do servidor é mais seguro, pois você tem a opção de não renderizar nenhum html.

Abra **pages/protected.js** e atualize com o seguinte código:  

```jsx
// pages/protected.js
import { withSSRContext } from 'aws-amplify'

function Protected({ username }) {
  return <h1>Hello {username} from SSR route!</h1>
}

export async function getServerSideProps({ req, res }) {
  const { Auth } = withSSRContext({ req })
  try {
    const user = await Auth.currentAuthenticatedUser()
    return {
      props: {
        authenticated: true,
        username: user.username
      }
    }
  } catch (err) {
    res.writeHead(302, { Location: '/profile' })
    res.end()
  }
  return {props: {}}
}

export default Protected
```

Ao tentar acessar esta rota, você será redirecionado para a rota do **/profile** se não estiver autenticado.

## Login social (OAuth)

Para adicionar login social, execute `amplify update auth` e escolha **Apply default configuration with Social Provider**.

A partir daqui, você pode adicionar login social com Google, Facebook ou Amazon.

Depois que o login social for ativado, você poderá conectar os usuários do seu aplicativo usando o seguinte código: 

```jsx
// nome de usuário e password + todos os provedores de OAuth
Auth.federatedSignIn()

// specifying an OAuth provider
<button onClick={() => Auth.federatedSignIn({provider: 'Facebook'})}>Open Facebook</button>
<button onClick={() => Auth.federatedSignIn({provider: 'Google'})}>Open Google</button>
<button onClick={() => Auth.federatedSignIn({provider: 'Amazon'})}>Open Amazon</button>

```

## Implantar o aplicativo Next.js na AWS com o Serverless Framework

Para implantar o aplicativo na AWS usando o Serverless Framework e o [Serverless Next Component](https://github.com/serverless-nextjs/serverless-next.js) , primeiro crie um arquivo chamado `serverless.yml` na raiz do seu aplicativo.

Em seguida, adicione as duas linhas de configuração a seguir (sinta-se à vontade para alterar **myNextApp** para qualquer nome que você gostaria de usar):  

```yml
myNextApp:
    component: "@sls-next/serverless-component@1.16.0" 
```

Em seguida, implante usando `npx`:  

```bash
npx serverless
```

> Se você nunca usou um AWS CLI, terá que configurar suas credenciais AWS. Veja as instruções básicas [aqui](https://github.com/serverless-nextjs/serverless-next.js#getting-started).

## Passo a passo de vídeo

{% youtube S25rmxiOBz4 %}

O código final para este projeto está localizado [aqui](https://github.com/dabit3/next.js-authentication-aws).

Um grande obrigado para [Eric Clemmons](https://twitter.com/ericclemmons) da equipe do Amplify que liderou este projeto e construiu essa funcionalidade no Amplify.

Para a parte 2, aprenderemos como combinar Auth e Data para realizar o seguinte:

1.  Buscar dados em `getStaticPaths` para hidratação durante SSG
2.  Fazer chamadas de API autenticadas em rotas de API
3.  Fazer uma solicitação de API autenticada em `getServerSideProps`