Skip to content

Autenticação social

Você pode implementar autenticação social em seus aplicativos AdonisJS usando o pacote @adonisjs/ally. O Ally vem com os seguintes drivers integrados, juntamente com uma API extensível para registrar drivers personalizados.

  • Twitter
  • Facebook
  • Spotify
  • Google
  • GitHub
  • Discord
  • LinkedIn

O Ally não armazena nenhum usuário ou token de acesso em seu nome. Ele implementa os protocolos OAuth2 e OAuth1, autentica um usuário com serviço social e fornece detalhes do usuário. Você pode armazenar essas informações dentro de um banco de dados e usar o pacote auth para fazer login do usuário em seu aplicativo.

Instalação

Instale e configure o pacote usando o seguinte comando:

sh
node ace add @adonisjs/ally

# Definir provedores como sinalizadores CLI
node ace add @adonisjs/ally --providers=github --providers=google

Veja as etapas executadas pelo comando add

  1. Instala o pacote @adonisjs/ally usando o gerenciador de pacotes detectado.

  2. Registra o seguinte provedor de serviços dentro do arquivo adonisrc.ts.

    ts
    {
      providers: [
        // ...outros provedores
        () => import('@adonisjs/ally/ally_provider')
      ]
    }
  3. Crie o arquivo config/ally.ts. Este arquivo contém as configurações para provedores OAuth selecionados.

  4. Define as variáveis ​​de ambiente para armazenar CLIENT_ID e CLIENT_SECRET para provedores OAuth selecionados.

Configuração

A configuração do pacote @adonisjs/ally é armazenada dentro do arquivo config/ally.ts. Você pode definir a configuração para vários serviços em um único arquivo de configuração.

Veja também: Config stub

ts
import { defineConfig, services } from '@adonisjs/ally'

defineConfig({
  github: services.github({
    clientId: env.get('GITHUB_CLIENT_ID')!,
    clientSecret: env.get('GITHUB_CLIENT_SECRET')!,
    callbackUrl: '',
  }),
  twitter: services.twitter({
    clientId: env.get('TWITTER_CLIENT_ID')!,
    clientSecret: env.get('TWITTER_CLIENT_SECRET')!,
    callbackUrl: '',
  }),
})

Configurando a URL de retorno de chamada

Os provedores OAuth exigem que você registre uma URL de retorno de chamada para manipular a resposta de redirecionamento após o usuário autorizar a solicitação de login.

A URL de retorno de chamada deve ser registrada com o provedor de serviços OAuth. Por exemplo: se você estiver usando o GitHub, você deve fazer login na sua conta do GitHub, criar um novo aplicativo e definir a URL de retorno de chamada usando a interface do GitHub.

Além disso, você deve registrar a mesma URL de retorno de chamada dentro do arquivo config/ally.ts usando a propriedade callbackUrl.

Uso

Depois que o pacote for configurado, você pode interagir com as APIs do Ally usando a propriedade ctx.ally. Você pode alternar entre os provedores de autenticação configurados usando o método ally.use(). Por exemplo:

ts
router.get('/github/redirect', ({ ally }) => {
  // Instância do driver GitHub
  const gh = ally.use('github')
})

router.get('/twitter/redirect', ({ ally }) => {
  // Instância do driver Twitter
  const twitter = ally.use('twitter')
})

// Você também pode recuperar dinamicamente o driver
router.get('/:provider/redirect', ({ ally, params }) => {
  const driverInstance = ally.use(params.provider)
}).where('provider', /github|twitter/)

Redirecionando o usuário para autenticação

O primeiro passo na autenticação social é redirecionar o usuário para um serviço OAuth e esperar que ele aprove ou negue a solicitação de autenticação.

Você pode executar o redirecionamento usando o método .redirect().

ts
router.get('/github/redirect', ({ ally }) => {
  return ally.use('github').redirect()
})

Você pode passar uma função de retorno de chamada para definir escopos personalizados ou valores de string de consulta durante o redirecionamento.

ts
router.get('/github/redirect', ({ ally }) => {
  return ally
    .use('github')
    .redirect((request) => {
      request.scopes(['user:email', 'repo:invite'])
      request.param('allow_signup', false)
    })
})

Lidando com a resposta de retorno de chamada

O usuário será redirecionado de volta para o callbackUrl do seu aplicativo após aprovar ou negar a solicitação de autenticação.

Nesta rota, você pode chamar o método .user() para obter os detalhes do usuário conectado e o token de acesso. No entanto, você também deve verificar a resposta para possíveis estados de erro.

ts
router.get('/github/callback', async ({ ally }) => {
  const gh = ally.use('github')

  /**
   * O usuário negou o acesso cancelando
   * o fluxo de login
   */
  if (gh.accessDenied()) {
    return 'You have cancelled the login process'
  }

  /**
   * A verificação do estado OAuth falhou. Isso acontece quando o
   * cookie CSRF expira.
   */
  if (gh.stateMisMatch()) {
    return 'We are unable to verify the request. Please try again'
  }

  /**
   * O GitHub respondeu com algum erro
   */
  if (gh.hasError()) {
    return gh.getError()
  }

  /**
   * Acessar informações do usuário
   */
  const user = await gh.user()
  return user
})

Propriedades do usuário

A seguir está a lista de propriedades que você pode acessar a partir do valor de retorno da chamada do método .user(). As propriedades são consistentes entre todos os drivers subjacentes.

ts
const user = await gh.user()

user.id
user.email
user.emailVerificationState
user.name
user.nickName
user.avatarUrl
user.token
user.original

id

Um ID exclusivo retornado pelo provedor OAuth.

email

O endereço de e-mail retornado pelo provedor OAuth. O valor será null se a solicitação OAuth não solicitar o endereço de e-mail do usuário.

emailVerificationState

Muitos provedores OAuth permitem que usuários com e-mails não verificados façam login e autentiquem solicitações OAuth. Você deve usar este sinalizador para garantir que apenas usuários com e-mails verificados possam fazer login.

A seguir está a lista de valores possíveis.

  • verified: O endereço de e-mail do usuário é verificado com o provedor OAuth.
  • unverified: O endereço de e-mail do usuário não é verificado.
  • unsupported: O provedor OAuth não compartilha o estado de verificação de e-mail.

name

O nome do usuário retornado pelo provedor OAuth.

nickName

Um apelido publicamente visível do usuário. O valor de nickName e name será o mesmo se o provedor OAuth não tiver nenhum conceito de apelidos.

avatarUrl

A URL HTTP(s) para a foto do perfil público do usuário.

token

A propriedade token é a referência ao objeto de token de acesso subjacente. O objeto token tem as seguintes subpropriedades.

ts
user.token.token
user.token.type
user.token.refreshToken
user.token.expiresAt
user.token.expiresIn
PropriedadeProtocoloDescrição
tokenOAuth2 / OAuth1O valor do token de acesso. O valor está disponível para os protocolos OAuth2 e OAuth1.
secretOAuth1O segredo do token aplicável somente para o protocolo OAuth1. Atualmente, o Twitter é o único driver oficial usando OAuth1.
typeOAuth2O tipo de token. Normalmente, será um token portador.
refreshTokenOAuth2Você pode usar o token de atualização para criar um novo token de acesso. O valor será undefined se o provedor OAuth não suportar tokens de atualização
expiresAtOAuth2Uma instância da classe luxon DateTime representando o tempo absoluto em que o token de acesso irá expirar.
expiresInOAuth2Valor em segundos, após o qual o token irá expirar. É um valor estático e não muda com o passar do tempo.

original

Referência à resposta original do provedor OAuth. Você pode querer referenciar a resposta original se o conjunto normalizado de propriedades do usuário não tiver todas as informações que você precisa.

ts
const user = await github.user()
console.log(user.original)

Definindo escopos

Escopos referem-se aos dados que você deseja acessar após o usuário aprovar a solicitação de autenticação. O nome dos escopos e os dados que você pode acessar variam entre os provedores OAuth; portanto, você deve ler a documentação deles.

Os escopos podem ser definidos dentro do arquivo config/ally.ts, ou você pode defini-los ao redirecionar o usuário.

Graças ao TypeScript, você obterá sugestões de preenchimento automático para todos os escopos disponíveis.

ts
// config/ally.ts

github: {
  driver: 'github',
  clientId: env.get('GITHUB_CLIENT_ID')!,
  clientSecret: env.get('GITHUB_CLIENT_SECRET')!,
  callbackUrl: '',
  scopes: ['read:user', 'repo:invite'],
}
ts
// Durante o redirecionamento

ally
  .use('github')
  .redirect((request) => {
    request.scopes(['read:user', 'repo:invite'])
  })

Definindo parâmetros de consulta de redirecionamento

Você pode personalizar os parâmetros de consulta para a solicitação de redirecionamento junto com os escopos. No exemplo a seguir, definimos os parâmetros prompt e access_type aplicáveis ​​com o provedor do Google.

ts
router.get('/google/redirect', async ({ ally }) => {
  return ally
    .use('google')
    .redirect((request) => {
      request
        .param('access_type', 'offline')
        .param('prompt', 'select_account')
    })
})

Você pode limpar quaisquer parâmetros existentes usando o método .clearParam() na solicitação. Isso pode ser útil se os padrões de parâmetros forem definidos na configuração e você precisar redefini-los para um fluxo de autenticação personalizado separado.

ts
router.get('/google/redirect', async ({ ally }) => {
  return ally
    .use('google')
    .redirect((request) => {
      request
        .clearParam('redirect_uri')
        .param('redirect_uri', '')
    })
})

Obtendo detalhes do usuário de um token de acesso

Às vezes, você pode querer buscar detalhes do usuário de um token de acesso armazenado no banco de dados ou fornecido por outro fluxo OAuth. Por exemplo, você usou o fluxo Native OAuth por meio de um aplicativo móvel e recebeu um token de acesso de volta.

Você pode buscar os detalhes do usuário usando o método .userFromToken().

ts
const user = await ally
  .use('github')
  .userFromToken(accessToken)

Você pode buscar os detalhes do usuário para um driver OAuth1 usando o método .userFromTokenAndSecret.

ts
const user = await ally
  .use('github')
  .userFromTokenAndSecret(token, secret)

Autenticação sem estado

Muitos provedores OAuth recomendam usar um token de estado CSRF para evitar que seu aplicativo sofra ataques de falsificação de solicitação.

O Ally cria um token CSRF e o salva dentro de um cookie criptografado, que é verificado depois que o usuário aprova a solicitação de autenticação.

No entanto, se você não puder usar cookies por algum motivo, você pode habilitar o modo sem estado no qual nenhuma verificação de estado ocorrerá e, portanto, nenhum cookie CSRF será gerado.

ts
// Redirecionando

ally.use('github').stateless().redirect()
ts
// Manipulando resposta de retorno de chamada

const gh = ally.use('github').stateless()
await gh.user()

Referência de configuração completa

A seguir está a referência de configuração completa para todos os drivers. Você pode copiar e colar os seguintes objetos diretamente no arquivo config/ally.ts.

Configuração do GitHub
ts
{
  github: services.github({
    clientId: '',
    clientSecret: '',
    callbackUrl: '',

    // Específico do GitHub
    login: 'adonisjs',
    scopes: ['user', 'gist'],
    allowSignup: true,
  })
}
Configuração do Google
ts
{
  google: services.google({
    clientId: '',
    clientSecret: '',
    callbackUrl: '',

    // Específico do  Google
    prompt: 'select_account',
    accessType: 'offline',
    hostedDomain: 'adonisjs.com',
    display: 'page',
    scopes: ['userinfo.email', 'calendar.events'],
  })
}
Configuração do Twitter
ts
{
  twitter: services.twitter({
    clientId: '',
    clientSecret: '',
    callbackUrl: '',
  })
}
Configuração do Discord
ts
{
  discord: services.discord({
    clientId: '',
    clientSecret: '',
    callbackUrl: '',

    // Específico do Discord
    prompt: 'consent' | 'none',
    guildId: '',
    disableGuildSelect: false,
    permissions: 10,
    scopes: ['identify', 'email'],
  })
}
Configuração do LinkedIn
ts
{
  linkedin: services.linkedin({
    clientId: '',
    clientSecret: '',
    callbackUrl: '',

    // Específico do LinkedIn
    scopes: ['r_emailaddress', 'r_liteprofile'],
  })
}
Configuração do Facebook
ts
{
  facebook: services.facebook({
    clientId: '',
    clientSecret: '',
    callbackUrl: '',

    // Específico do Facebook
    scopes: ['email', 'user_photos'],
    userFields: ['first_name', 'picture', 'email'],
    display: '',
    authType: '',
  })
}
Configuração do Spotify
ts
{
  spotify: services.spotify({
    clientId: '',
    clientSecret: '',
    callbackUrl: '',

    // Específico do Spotify
    scopes: ['user-read-email', 'streaming'],
    showDialog: false
  })
}

Criando um driver social personalizado

Criamos um kit inicial para implementar e publicar um driver social personalizado no npm. Leia o README do kit inicial para obter mais instruções.