close icon
Angular

O Guia Completo de Angular User Authentication com Auth0

Aprenda como adicionar autenticação de usuário ao Angular usando Observables e HTTP Interceptors.

November 17, 2022

Este guia dá suporte a aplicações Angular 9+. O suporte de longo prazo (Long Term Support - LTS) para Angular 8 terminou em 28 de novembro de 2020.


O foco deste tutorial é ajudar pessoas desenvolvedoras a aprender como proteger uma aplicação Angular implementando a autenticação de usuário. Você vai melhorar uma aplicação Angular inicial para praticar os seguintes conceitos de segurança:

  • Adicionar login e logout de usuário.
  • Recuperar informações de usuário.
  • Proteger as rotas da aplicação.
  • Chamar endpoints protegidos de uma API.

Este tutorial usa o SDK da Auth0 para proteger as aplicações Angular. O SDK abstrai muitos detalhes de implementação de autenticação para ajudar você a seguir as melhores práticas de segurança usando uma abordagem Angular idiomática enquanto escreve menos código. Você não precisa ser especialista em OAuth 2.0 ou OpenID Connect para entender como proteger sua coleção de aplicações web.

Angular Sample Project

⏰⚡️ Se você tiver pouco tempo, confira O Guia de Início Rápido de Auth0 Angular.

Obtenha a Aplicação Inicial

Procure o emoji 🛠️ se quiser passar pelo conteúdo enquanto se concentra nas etapas de criação.

Criamos um projeto inicial usando a Interface de Linha de Comando (Command Line Interface - CLI) do Angular para te ajudar a aprender os conceitos de segurança do Angular através da prática. O projeto inicial usa Bootstrap com um tema personalizado para cuidar do estilo e do layout das suas aplicações para que você possa se concentrar na construção e ligação de componentes Angular.

🛠 Dessa forma, clone o repositório auth0-angular-sample em seu branch starter para começar:

git clone -b starter git@github.com:auth0-blog/auth0-angular-sample.git

🛠 Depois de clonar o repositório, torne auth0-angular-sample seu diretório atual:

cd auth0-angular-sample

🛠 Instale as dependências do projeto Angular:

npm install

🛠 Por último, inicie a aplicação Angular:

npm start

Conecte O Angular Com a Auth0

Por que usar Auth0 em vez de criar sua própria autenticação de usuário do zero?

No passado, pessoas sábias alertavam: "não inventarás a tua própria criptomoeda". Hoje, pessoas sábias aconselham que "você não precisa construir sua própria autenticação".

Construir um sistema detalhado de autenticação e autorização do zero é complexo. A Auth0 é uma plataforma Identity-as-a-Service (IDaaS) que permite centralizar a autenticação de usuário e a autorização da API para todas as suas aplicações, a fim de reduzir essa complexidade.

A Auth0 oferece recursos de segurança poderosos prontos para usar. Uma página de login personalizável, login social, autenticação multifator (MFA) e gerenciamento avançado de usuários permitem que você coloque a sua aplicação no ar em tempo recorde. Talvez o recurso mais importante seja a detecção de anomalias, que ajuda a combater ataques de credential stuffing .

Na Auth0, os ataques de preenchimento de credenciais representam, em média, quase metade de todas as tentativas de login usando nossa plataforma. Leia mais detalhes sobre esse vetor de ataque crítico: Ataques de preenchimento de credenciais: o que são e como combatê-los (em inglês).

Como a Auth0 funciona?

  • Você começa integrando a Auth0 com sua aplicação Angular.
  • Quando seus usuários precisam fazer login, sua aplicação Angular aciona um evento de autenticação, que funciona redirecionando eles para uma página de login Auth0 personalizável.
  • Depois que seus usuários fizerem login com sucesso, a Auth0 os redireciona de volta à sua aplicação Angular, retornando tokens com a autenticação e informações do usuário.
  • Além disso, você pode proteger suas APIs com Auth0 para poder usar um token de acesso para fazer uma requisição da sua aplicação Angular para seus endpoints de API protegidos.

O quão fácil é começar?

Muito fácil! Só seguir esses passos:

Configure uma Aplicação Auth0

Se ainda não o fez, cadastre-se para obter uma conta Auth0 gratuita →

Uma conta gratuita oferece:

Durante o cadastro, você cria um Auth0 Tenant, que é um container que a Auth0 usa para armazenar sua configuração de serviço de identidade e seus usuários isoladamente. Nenhum outro cliente Auth0 pode espiar ou acessar seu tenant.

🛠 Depois de se inscrever, Auth0 leva você para o Dashboard, onde você pode gerenciar e configurar seus serviços de identidade. No menu da barra lateral esquerda, clique em "Applications".

🛠 Em seguida, clique no botão "Create Application". Irá abrir um formulário para que você escreva o nome e escolha o tipo da aplicação.

  • Name: (Nome)
Auth0 Angular Sample
  • Application Type: (Tipo da aplicação)
Single Page Web Applications

🛠 Clique no botão "Create" para concluir o processo. A página da sua aplicação Auth0 é carregada.

Na próxima etapa, você aprenderá como ajudar o Angular e a Auth0 a se comunicarem usando os dados de configuração dessa página — não a feche ainda.

Qual é a relação entre Auth0 Tenants e aplicações Auth0?

Digamos que você tenha uma aplicação Angular de compartilhamento de fotos chamado “Angulogram”. Você então criaria um Auth0 tenant chamado angulogram.

Agora, digamos que o Angulogram esteja disponível em três plataformas: na web como uma aplicação de página única (Single Page Application ou SPA em inglês) e como um aplicativo móvel nativo para Android e iOS. Se cada plataforma precisar de autenticação, você precisará criar três aplicações Auth0 para fornecer ao produto tudo o que a pessoa precisa para autenticar os usuários por meio dessa plataforma.

Crie uma ponte de comunicação entre Angular e Auth0

Ao usar a Auth0, você não precisa criar formulários de login. A Auth0 oferece uma página de login universal para reduzir a sobrecarga de adição e gerenciamento de autenticação.

É importante destacar que o formulário fornecido pela Auth0 (Auth0 Universal Login) reduz o risco de enumeração de nome de usuário e senha. O Login Universal da Auth0 implementa corretamente as mensagens de erro de autenticação seguindo as recomendações da OWASP (The Open Web Application Security Project): diga o suficiente para ajudar o usuário que está tentando entrar, mas não diga demais para evitar ajudar quem tenta invadir.

Mensagem de erro de autenticação do login universal da Auth0

Como funciona oLogin Universal?

Sua aplicação Angular redirecionará os usuários para a Auth0 sempre que eles dispararem uma requisição de autenticação. A Auth0 irá apresentar uma página de login. Assim que eles fizerem login, a Auth0 os redirecionará de volta para a sua aplicação. Para que o redirecionamento ocorra com segurança, você deve especificar nas suas configurações de aplicações Auth0 as URLs para os quais a Auth0 pode redirecionar usuários, uma vez que estejam autenticados.

🛠 Clique na guia "Settings" da página da aplicação Auth0 e preencha os seguintes valores:

🛠 URLs de callback permitidas (Allowed Callback URLs em inglês)

http://localhost:4040

Depois que seus usuários fizerem login, a Auth0 só pode redirecioná-los para alguma das URLs que você listar aqui.

🛠URLs de logout permitidas (Allowed Logout URLs em inglês)

http://localhost:4040

Depois que seus usuários fizerem logout, a Auth0 só pode redirecioná-los para alguma das URLs que você listar aqui.

🛠 Origens Web permitidas (Allowed Web Origins em inglês)

http://localhost:4040

Usando o SDK da Auth0 para Angular, sua aplicação Angular fará requisições internas a uma URL da Auth0 para lidar com as requisições de autenticação. Você precisa adicionar a URL de origem da aplicação Angular para evitar problemas de Compartilhamento de recursos de origem cruzada (Cross-Origin Resource Sharing - CORS).

🛠 Role para baixo e clique no botão "Save changes".

🛠 Não feche esta página ainda. Você precisará de algumas informações na próxima seção.

Adicione as variáveis de configuração da Auth0 ao Angular

Na aba "Settings" da página da aplicação Auth0, você precisa dos valores de Domínio Auth0 (Auth0 Domain em inglês) e ID do Cliente (Client ID em inglês) para permitir que sua aplicação Angular use a ponte de comunicação que você criou.

O que exatamente é um domínio Auth0 e um ID de cliente Auth0?

Domínio Quando você criou uma nova conta Auth0, a Auth0 pediu para escolher um nome para seu tenant. Este nome, anexado com auth0.com, é o seu domínio Auth0 (Auth0 Domain em inglês). Depois de anexar o protocolo https://, você obtém a URL base que sua aplicação Angular pode usar para redirecionar os usuários para fazer login e acessar as APIs Auth0:

https://<TENANT-NAME>.auth0.com

Auth0 também oferece suporte a domínios personalizados para você preservar sua marca durante o processo de autenticação.

ID de Cliente

A Auth0 atribui um ID de cliente (Client ID) a cada aplicação que você cria no Dashboard da Auth0. O ID de cliente é uma string alfanumérica, e é o identificador exclusivo da sua aplicação no seu Auth0 tentant (como q8fij2iug0CmgPLfTfG1tZGdTQyGaTUA). Você não pode modificar o ID do cliente. Você usará o ID do cliente para identificar a aplicação Auth0 que o SDK da Auth0 para Angular precisa se conectar.

Atenção: Outra informação crítica presente nas "Settings" é o Segredo do Cliente (Client Secret em inglês). Esse segredo protege seus recursos concedendo tokens apenas para solicitantes se tiverem autorização. Pense nisso como a senha da sua aplicação, que deve ser mantida em sigilo o tempo todo. Se alguém obtiver acesso ao seu segredo, poderá se passar pela sua aplicação e acessar recursos protegidos.

🛠 Abra o projeto inicial do Angular, auth0-angular-sample e crie um arquivoauth_config.json no diretório do projeto:

  • macOS/Linux:
touch auth_config.json
  • Windows Powershell:
ni auth_config.json

🛠 Preencha o auth_config.json da seguinte forma:

{
  "domain": "YOUR_AUTH0_DOMAIN",
  "clientId": "YOUR_AUTH0_CLIENT_ID"
}

🛠 Volte para a página da aplicação Auth0. Siga estas etapas para obter os valores de domain e clientId:

Configurações da aplicação Auth0 para habilitar autenticação de usuário

  1. 🛠 Clique na guia "Settings", se ainda não tiver feito isso.
  2. 🛠 Use o valor "Domain" em "Settings" como o valor de domain in auth_config.json.
  3. 🛠 Use o valor "Client ID" em "Settings" como o valor de clientId no auth_config.json.

Essas variáveis permitem que sua aplicação Angular se identifique como uma parte autorizada para interagir com o servidor de autenticação Auth0 para continuar o processo. Você está mapeando a sua aplicação Angular para uma aplicação Auth0.

Para usar essas variáveis na sua aplicação Angular, você aproveitará o módulo environment Angular.

🛠 Substitua o conteúdo de src/environments/environment.ts com o seguinte:

// src/environments/environment.ts

import { domain, clientId } from '../../auth_config.json';

export const environment = {
  production: false,
  auth: {
    domain,
    clientId,
    redirectUri: window.location.origin,
  },
};

Como é possível importar arquivos JSON dentro de um módulo Angular? O projeto inicial tem um arquivo tsconfig.base.json que modifica o resolveJsonModule para true, o que permite que você importe e extraia arquivos .json.

Conexão Auth0 e Angular

Você concluiu a configuração de um serviço de autenticação que sua aplicação Angular pode consumir. Continue construindo o projeto inicial ao longo deste guia, adicionando componentes de segurança.

Fique à vontade para se aprofundar na documentação da Auth0 para saber mais sobre como a Auth0 ajuda a economizar tempo na implementação e gerenciamento de identidade.

Configure O SDK Da Auth0 Para Angular

🛠 Você precisa seguir estas etapas para integrar o SDK da Auth0 com sua aplicação Angular.

Instale o SDK da Auth0 para Angular

🛠 Execute o seguinte comando:

ng add @auth0/auth0-angular

O SDK da Auth0 para Angular expõe vários métodos, variáveis e tipos que ajudam você a integrar a Auth0 com sua aplicação Angular idiomaticamente, incluindo um módulo e serviço de autenticação.

Registre e configure o módulo de autenticação

O SDK exporta um módulo com os componentes e serviços necessários para realizar a autenticação do usuário. Importe este módulo para o AppModule para acessá-lo por meio do framework de injeção de dependência do Angular.

🛠 Importe AuthModule e environment acima da definição @NgModule em src/app/app.module.ts dessa forma:

// src/app/app.module.ts

// Other imports...

import { AuthModule } from '@auth0/auth0-angular';
import { environment as env } from '../environments/environment';

@NgModule({...})
export class AppModule {}

🛠 Então, adicione AuthModule ao AppModule que foi importado e o inicialize:

// src/app/app.module.ts

// All imports...

@NgModule({
  declarations: [...],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    FontAwesomeModule,
    // 👇 add and initialize AuthModule
    AuthModule.forRoot({
      ...env.auth,
    }),
  ],
  bootstrap: [...],
})
export class AppModule {}

Você usa o padrão forRoot() para configurar AuthModule, que obtém um objeto com as propriedades domain e clientId. Você cria esse objeto de configuração usando o objeto env.auth.

No entanto, a autenticação de usuário é um mecanismo para monitorar quem está acessando sua aplicação e controlar o que essas pessoas podem fazer. Por exemplo, você pode impedir que usuários que não tenham efetuado login acessem partes da sua aplicação. Nesse cenário, a Auth0 pode atuar como bouncer da aplicação.

Um bouncer é uma pessoa empregada por uma boate ou estabelecimento similar para impedir que encrenqueiros entrem ou os expulsem do local. A segurança do Angular não é muito diferente da segurança de uma boate.

Se os usuários quiserem acessar uma rota protegida da sua aplicação, a Auth0 irá interrompê-los e solicitar que apresentem suas credenciais. Se a Auth0 puder verificar quem são e se devem entrar lá, a Auth0 os deixará entrar. Caso contrário, a Auth0 os levará de volta para uma rota pública da aplicação.

Agora, é importante reiterar que o processo de autenticação não acontecerá na sua aplicação. Sua aplicação Angular redirecionará seus usuários para a página de login universal da Auth0, onde a Auth0 solicita as credenciais e redireciona o usuário de volta à sua aplicação com o resultado do processo de autenticação.

O SDK da Auth0 para Angular está configurado. Tudo pronto para criar componentes para implementar o fluxo de autenticação na próxima seção.

Adicione Autenticação De Usuários

O SDK da Auth0 para Angular fornece métodos para acionar eventos de autenticação: login, logout e inscrição.

Crie um botão de login

🛠 Crie um LoginButtonComponent dentro do diretório src/components/ usando a Angular CLI:

ng generate component components/login-button --inlineStyle --skipTests

🛠 Preencha o arquivo login-button.component.ts no diretório src/app/components/login-button/ assim:

// src/app/components/login-button/login-button.component.ts

import { Component, OnInit } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';

@Component({
  selector: 'app-login-button',
  templateUrl: './login-button.component.html',
  styles: [],
})
export class LoginButtonComponent implements OnInit {
  constructor(public auth: AuthService) {}

  ngOnInit(): void {}

  loginWithRedirect(): void {
    this.auth.loginWithRedirect();
  }
}

🛠 Depois, preencha o template login-button.component.html no diretório src/app/components/login-button/ dessa forma:

<!-- src/app/components/login-button/login-button.component.html -->

<button class="btn btn-primary btn-block" (click)="loginWithRedirect()">
  Log in
</button>

Dentro da definição de LoginButtonComponent, auth.loginWithRedirect() é um método exposto pelo AuthService. Chamar esse método solicita que um usuário autentique e dê consentimento para que sua aplicação Angular acesse determinados dados em nome deste usuário. Em sua arquitetura atual, isso significa que sua aplicação Angular redireciona o usuário para a página de login universal da Auth0 para realizar o processo de autenticação. Você verá isso em ação nas próximas seções.

Você pode passar um objeto de configuração para loginWithRedirect() para personalizar a experiência de login. Por exemplo, você pode passar opções para loginWithRedirect() redirecionar usuários a uma página de login universal Auth0 otimizada para se inscrever na sua aplicação Angular. Consulte RedirectLoginOptions (em inglês) para obter mais detalhes sobre essas opções.

Crie um botão de inscrição

Você pode fazer com que os usuários acessem diretamente uma página de inscrição em vez de uma página de login, especificando a propriedade screen_hint no objeto de configuração auth.loginWithRedirect():

{
  screen_hint: "signup",
}

🛠 Crie um SignupButtonComponent no diretório src/components/ usando Angular CLI:

ng generate component components/signup-button --inlineStyle --skipTests

🛠 Preencha o arquivo signup-button.component.ts em src/app/components/signup-button/ assim:

// src/app/components/signup-button/signup-button.component.ts

import { Component, OnInit } from '@angular/core';

import { AuthService } from '@auth0/auth0-angular';

@Component({
  selector: 'app-signup-button',
  templateUrl: './signup-button.component.html',
})
export class SignupButtonComponent implements OnInit {
  constructor(public auth: AuthService) {}

  ngOnInit(): void {}

  loginWithRedirect(): void {
    this.auth.loginWithRedirect({ screen_hint: 'signup' });
  }
}

🛠 Preencha o arquivo base signup-button.component.html em src/app/components/signup-button/ dessa forma:

<button class="btn btn-primary btn-block" (click)="loginWithRedirect()">
  Sign Up
</button>

O uso do recurso de inscrição exige que você ative o Auth0 New Universal Login Experience em seu tenant.

🛠 Abra a seção Login Universal do dashboard Auth0 e escolha a opção "New" na subseção "Experience".

Opções da Auth0 Universal Login Experience

🛠 Role para baixo e clique no botão "Save changes".

A diferença entre a experiência de usuário em LoginButtonComponent e SignupButtonComponent ficará mais evidente quando você integrar esses componentes à aplicação Angular e vê-los em ação. Você fará isso nas próximas seções

Crie um botão de logout

🛠 Crie um LogoutButtonComponent no diretório src/components/:

ng generate component components/logout-button --inlineStyle --skipTests

🛠 Preencha o arquivo logout-button.component.ts em src/app/components/logout-button/ com o código:

// src/app/components/logout-button/logout-button.component.ts

import { Component, Inject, OnInit } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { DOCUMENT } from '@angular/common';

@Component({
  selector: 'app-logout-button',
  templateUrl: './logout-button.component.html',
  styles: [],
})
export class LogoutButtonComponent implements OnInit {
  constructor(
    public auth: AuthService,
    @Inject(DOCUMENT) private doc: Document,
  ) {}

  ngOnInit(): void {}

  logout(): void {
    this.auth.logout({ returnTo: this.doc.location.origin });
  }
}

Você está definindo um método logout() que aciona o evento fazer logout. Você passa pra ele um objeto de configuração opcional para informar à Auth0 para onde levar os usuários depois de fazer o logout.

🛠 Depois, preencha o arquivo base logout-button.component.html em src/app/components/logout-button/ dessa forma:

<!--src/app/components/logout-button/logout-button.component.html-->

<button class="btn btn-danger btn-block" (click)="logout()">Log out</button>

O método auth.logout() exposto por AuthService limpa a sessão da aplicação e redireciona para o endpoint Auth0 /v2/logout para limpar a sessão Auth0. Com os métodos de login, você pode passar um argumento de objeto para logout() para definir parâmetros para a chamada /v2/logout. Este processo é praticamente invisível para o usuário. Consulte LogoutOptions (em inglês) para obter mais detalhes.

Aqui, você adiciona a propriedade returnTo para especificar a URL onde Auth0 deve redirecionar seus usuários após o logout. No momento, você está trabalhando localmente e as "Allowed Logout URLs" da sua aplicação Auth0 apontam para http://localhost:4040.

No entanto, se você for fazer o deploy da sua aplicação Angular para produção, será necessário adicionar a URL de logout de produção à lista "Allowed Logout URLs" e garantir que a Auth0 redirecione seus usuários para essa URL de produção e não para o localhost. Definir returnTo como this.doc.location.origin fará exatamente isso.

Os componentes Angular não têm acesso direto ao objeto document. No entanto, você pode fazer @Inject na constante DOCUMENT como uma dependência de AuthenticationButtonComponent. this.doc é igual ao documento DOM o no navegador. this.doc.location retorna um objeto Location cuja propriedade origin é a origem da sua aplicação.

Leia mais sobre como funciona o Logout na Auth0 (em inglês).

Integre os botões de login e logout

Vamos envolver o LoginButtonComponent e LogoutButtonComponent em um único componente que tem lógica para decidir qual botão renderizar dependendo do status de autenticação do usuário.

🛠 Crie AuthenticationButtonComponent no diretório src/app/components/:

ng g c components/authentication-button --inlineStyle --skipTests

Você está usando a abreviação g (generate) e c (component) para tornar o comando mais curto.

🛠 Preencha o arquivo authentication-button.component.ts no diretório src/app/components/authentication-button/ assim:

// src/app/components/authentication-button/authentication-button.component.ts

import { Component, OnInit } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';

@Component({
  selector: 'app-authentication-button',
  templateUrl: './authentication-button.component.html',
  styles: [],
})
export class AuthenticationButtonComponent implements OnInit {
  constructor(public auth: AuthService) {}

  ngOnInit(): void {}
}

🛠 Preencha o arquivo authentication-button.component.html no diretório src/app/components/authentication-button/ dessa forma:

<!--
src/app/components/authentication-button/
  authentication-button.component.html
-->

<app-login-button *ngIf="(auth.isAuthenticated$ | async) === false">
</app-login-button>

<app-logout-button *ngIf="auth.isAuthenticated$ | async"> </app-logout-button>

Vamos começar entendendo o que está acontecendo no arquivo base. auth.isAuthenticated$ é um Observable exposto por AuthService que emite um valor booleano. Seu valor é true quando a Auth0 autenticou o usuário e false quando não o fez.

É importante observar que, por baixo dos panos, auth.isAuthenticated$ só começa a emitir valores quando o SDK da Auth0 para Angular termina de carregar. Quando AuthService.isLoading$ emite false, então auth.isAuthenticated$ emite seu valor. Esse encadeamento de operações ajuda a evitar falsos positivos em relação ao status de autenticação de um usuário. Isso também causa um pequeno atraso na renderização do AuthenticationButtonComponent, mas você corrigirá isso em breve.

Existem algumas vantagens em usar o wrapper de componente AuthenticationButtonComponent:

Você pode construir interfaces flexíveis. AuthenticationButtonComponent serve como uma opção de "login/logout" que você pode colocar em qualquer lugar que precisar dessa funcionalidade de troca. No entanto, você ainda tem componentes LoginButtonComponent e LogoutButtonComponent separados para os casos em que você precise de sua funcionalidade de forma isolada. Por exemplo, você pode ter um botão de logout em uma página que somente usuários autenticados podem ver.

Você pode construir interfaces extensíveis. Você pode facilmente trocar o componente LoginButtonComponent por SignupButtonComponent em AuthenticationButtonComponent para criar uma opção de "sign up/log out". Você também pode envolver a opção "sign up/log out" em um componente NewAuthenticationButtonComponent se quiser.

Você pode construir interfaces declarativas. Usando AuthenticationButtonComponent, você pode adicionar a funcionalidade de login e logout ao seu componente NavBarComponent, por exemplo, sem pensar nos detalhes de implementação de como a troca de autenticação funciona.

🛠 Com isso em mente, crie um AuthNavComponent no diretóriosrc/components/:

ng g c components/auth-nav --inlineStyle --skipTests

🛠 Preencha o arquivo auth-nav.component.html no diretório src/app/components/auth-nav/ assim:

<!--src/app/components/auth-nav/auth-nav.component.html-->

<div class="navbar-nav ml-auto">
  <app-authentication-button></app-authentication-button>
</div>

🛠 Finalmente, abra o arquivo base nav-bar.component.html no diretório src/app/components/nav-bar/ e atualize-o assim:

<!--src/app/components/nav-bar/nav-bar.component.html-->

<div class="nav-container mb-3">
  <nav class="navbar navbar-expand-md navbar-light bg-light">
    <div class="container">
      <div class="navbar-brand logo"></div>
      <app-main-nav></app-main-nav>
      <app-auth-nav></app-auth-nav>
    </div>
  </nav>
</div>

Por ter diferentes tipos de subcomponentes da barra de navegação, você pode estender cada um conforme necessário, sem reabrir e modificar o componente MainNavComponent.

🛠 Vá em frente e tente fazer o login. Sua aplicação Angular redireciona você para a página de login universal da Auth0. Você pode usar um formulário para fazer login com um nome de usuário e senha ou um provedor de identidade social como o Google. Observe que esta página de login também oferece a opção de se inscrever.

Formulário da nova experiência do Login Universal Auth0

Experimento: Use SignupButtonComponent

Troque o componente LoginButtonComponent pelo componente SignupButtonComponent no arquivo base AuthenticationButtonComponent.

Ao clicar no botão "Sign Up" (Inscreva-se), você será direcionado para uma página com linguagem otimizada para incentivar a se inscrever em sua aplicação Angular.

Experimente isso!

Nova página de inscrição na Experiência de Login Universal Auth0

Depois de concluir esse teste, troque SignupButtonComponent por LoginButtonComponent para continuar com o restante deste guia.

Você pode personalizar a aparência das novas páginas de Login Universal. Você também pode substituir qualquer texto na "New Experience" usando a API de Personalização de Texto.

Observe que, quando você termina de fazer login e a Auth0 redireciona você para sua aplicação Angular, a interface do usuário apresenta uma tela piscando.

A interface do usuário "pisca" porque sua aplicação Angular está carregando os serviços. Enquando ela carrega, o Angular não sabe se a Auth0 já autenticou o usuário. Sua aplicação saberá o status de autenticação do usuário depois que o SDK da Auth0 para Angular for carregado.

🛠 Para corrigir a intermitência da interface de usuário (UI), use o Observable auth.isLoading$ exposto pelo AuthService que emite um valor booleano para renderizar o componente AppComponent assim que o SDK da Auth0 para Angular terminar de carregar.

🛠 Abra o arquivo src/app/app.component.ts e atualize-o da seguinte forma:

// src/app/app.component.ts

import { Component } from '@angular/core';

import { AuthService } from '@auth0/auth0-angular';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
})
export class AppComponent {
  constructor(public auth: AuthService) {}
}

🛠 Abra o arquivo src/app/app.component.html e atualize-o da seguinte forma:

<!--src/app/app.component.html-->

<div id="app" class="d-flex flex-column h-100">
  <div class="container" *ngIf="auth.isLoading$ | async; else loaded">
    <app-loading></app-loading>
  </div>

  <ng-template #loaded>
    <app-nav-bar></app-nav-bar>

    <div class="container flex-grow-1">
      <div class="mt-5">
        <router-outlet></router-outlet>
      </div>
    </div>

    <app-footer></app-footer>
  </ng-template>
</div>

Enquanto o SDK está carregando, o componente de carregamento LoadingComponent, que é animado, é renderizado.

Recupere Informações De Usuário

Depois que um usuário efetua login com sucesso, Auth0 envia um ID token para sua aplicação Angular. Os sistemas de autenticação, como Auth0, usam ID tokens na autenticação baseada em token para armazenar em cache as informações do perfil do usuário e fornecê-las a uma aplicação cliente. O armazenamento em cache de ID tokens pode contribuir para melhorias no desempenho e capacidade de resposta da sua aplicação Angular.

Você pode usar os dados do ID token para personalizar a interface do usuário da sua aplicação Angular. O SDK da Auth0 para Angular decodifica o ID token e apresenta seus dados através do Observable auth.user$ exposto pelo AuthService. Algumas das informações do ID token incluem o nome, apelido, imagem e e-mail do usuário conectado.

Como você pode usar o ID token para criar uma página de perfil para seus usuários?

🛠 Atualize o componente ProfileComponent em src/app/pages/profile/profile.component.ts da seguinte forma:

// src/app/pages/profile/profile.component.ts

import { Component, OnInit } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';

@Component({
  selector: 'app-profile',
  templateUrl: './profile.component.html',
})
export class ProfileComponent implements OnInit {
  profileJson: string = null;

  constructor(public auth: AuthService) {}

  ngOnInit(): void {
    this.auth.user$.subscribe(
      (profile) => (this.profileJson = JSON.stringify(profile, null, 2)),
    );
  }
}

🛠 Atualize o componente base ProfileComponent em src/app/pages/profile/profile.component.html da seguinte forma:

<!--src/app/pages/profile/profile.component.html-->

<div *ngIf="auth.user$ | async as user">
  <div class="row align-items-center profile-header">
    <div class="col-md-2 mb-3">
      <img
        [src]="user.picture"
        alt="User's profile picture"
        class="rounded-circle img-fluid profile-picture"
      />
    </div>
    <div class="col-md text-center text-md-left">
      <h2>{{ user.name }}</h2>
      <p class="lead text-muted">{{ user.email }}</p>
    </div>
  </div>

  <div class="row" *ngIf="profileJson">
    <pre class="col-12 text-light bg-dark p-4">{{ profileJson }}</pre>
  </div>
</div>

O que está acontecendo no componente ProfileComponent?

ngOnInit() é o melhor lugar para inicializar dados para um componente Angular. Assim, você assina o Observable this.auth.user$ no ProfileComponent. Depois que this.auth.user$ emite o objeto de perfil de usuário, você usa JSON.stringify para formatar o objeto e atribuí-lo a this.profileJson. Por sua vez, você usa a diretiva *ngIf para renderizar uma caixa de código com o objeto JSON de perfil de usuário com base no valor de profileJson.

O componente ProfileComponent renderiza informações do usuário que você pode considerar protegidas. Além disso, a propriedade do user é null se não houver um usuário conectado. Portanto, de qualquer forma, este componente só deve renderizar se a Auth0 tiver autenticado o usuário.

Como tal, você deve proteger a rota que renderiza este componente, http://localhost:4040/profile. Você aprenderá a fazer exatamente isso na próxima seção.

Proteja Rotas

De todas as seções deste guia, esta é a mais fácil de implementar graças à robustez do Angular Router. O SDK da Auth0 para Angular expõe um AuthGuard que você pode usar para proteger rotas.

🛠 Abra src/app/app-routing.module.ts e atualize-o da seguinte maneira:

// src/app/app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from 'src/app/pages/home/home.component';
import { ProfileComponent } from 'src/app/pages/profile/profile.component';
import { ExternalApiComponent } from 'src/app/pages/external-api/external-api.component';

import { AuthGuard } from '@auth0/auth0-angular';

const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
    pathMatch: 'full',
  },
  {
    path: 'profile',
    component: ProfileComponent,
    canActivate: [AuthGuard],
  },
  {
    path: 'external-api',
    component: ExternalApiComponent,
    canActivate: [AuthGuard],
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

Exigir login de usuário para acessar uma rota é fácil: basta incluir a propriedade canActivate na definição da rota e adicionar AuthGuard como seu valor. Quando os usuários que não efetuaram login acessarem essa rota, sua aplicação Angular os redirecionará para a página de login. Depois que o usuário fizer login, Auth0 redirecionará o usuário para sua aplicação Angular e o AuthService levará os usuários à página que eles desejavam acessar antes do login.

🛠 Agora você pode testar se esses dois caminhos, /profile e /external-api, exigem que os usuários façam login antes de acessá-los. Faça o log out e tente acessar a página Profile ou External API. Se funcionar, o Angular redireciona você para fazer o login com Auth0.

Os protetores do lado do cliente melhoram a experiência do usuário em sua aplicação Angular, não sua segurança.

No Security StackExchange, Conor Mancone explica que as proteções do lado do servidor visam proteger os dados, enquanto as proteções do lado do cliente visam melhorar a experiência do usuário.

As principais conclusões de sua resposta são:

  • Você não pode confiar em restrições do lado do cliente, como protetores de navegação e rotas protegidas, para proteger informações confidenciais.
    • Os invasores podem eventualmente contornar as restrições do lado do cliente.
  • Seu servidor não deve retornar nenhum dado que um usuário não deva acessar.
    • Retornar todos os dados do usuário do servidor e permitir que o framework de front-end decida o que exibir e o que ocultar com base no status de autenticação do usuário é uma abordagem errada.
    • Qualquer pessoa pode abrir as ferramentas de desenvolvimento do navegador e inspecionar as requisições de rede para visualizar todos os dados.
  • O uso de protetores de navegação ajuda a melhorar a experiência do usuário, não a segurança do usuário. - Sem protetores, um usuário que não fez login pode entrar em uma página com informações restritas e ver um erro, como "Acesso negado". - Com protetores que correspondem às permissões do servidor, você pode impedir que os usuários vejam erros, impedindo-os de visitar a página restrita.

Chame Uma API

Esta seção se concentra em mostrar como obter um token de acesso (access token, em inglês) na sua aplicação Angular e como usá-lo para fazer chamadas de API para endpoints protegidos.

Ao usar a Auth0, você delega o processo de autenticação a um serviço centralizado. A Auth0 oferece a funcionalidade de fazer login e logout de usuários da aplicação Angular. No entanto, sua aplicação pode precisar acessar recursos protegidos de uma API.

Você também pode proteger uma API com Auth0. Existem vários guias de início rápido de API para te ajudar a integrar a Auth0 com sua plataforma de back-end.

Ao usar a Auth0 para proteger sua API, você também delega o processo de autorização a um serviço centralizado que garante que apenas aplicações de cliente aprovadas possam acessar recursos protegidos em nome de um usuário.

Como você pode fazer chamadas de API seguras a partir do Angular?

Sua aplicação Angular autentica o usuário e recebe um token de acesso da Auth0. A aplicação pode então passar esse token de acesso para sua API como uma credencial. Por sua vez, sua API pode usar bibliotecas Auth0 para verificar o token de acesso que recebe da aplicação e emitir uma resposta com os dados desejados.

Em vez de criar uma API do zero para testar os fluxos de autenticação e autorização entre o cliente e o servidor, você usará uma API de demonstração Express API que preparei para você.

Obtenha a API de demonstração Express API

🛠 Abra o terminal em uma nova janela e clone o repositório auth0-express-js-sample em algum lugar do sistema. Certifique-se de fazer o clone fora do diretório do projeto Angular.

git clone git@github.com:auth0-blog/auth0-express-js-sample.git

🛠 Depois de clonar este repositório, torne o diretório auth0-express-js-sample seu diretório atual:

cd auth0-express-js-sample

🛠 Instale as dependências do projeto Node.js:

npm install

Conecte a Express API com Auth0

Crie uma ponte de comunicação entre Angular e Auth0

Este processo é semelhante a como você conectou o Angular à Auth0.

🛠 Vá para a seção APIs no Dashboard da Auth0 e clique no botão "Create API".

🛠 Então, no formulário que a Auth0 mostra:

  • Adicione um nome à sua API:
Auth0 Express Sample
  • Defina seu valor de identificador:
https://express.sample
  • Deixe o algoritmo de assinatura como RS256, pois é a melhor opção do ponto de vista de segurança.

formulário de nova API no Auth0 Dashboard

Identificadores são strings únicas que ajudam a Auth0 a diferenciar suas APIs. Recomendamos o uso de URLs para facilitar a criação de identificadores únicos de maneira previsível; no entanto, a Auth0 nunca chama essas URLs.

🛠 Com esses valores preenchidos, clique no botão "Create". Mantenha esta página aberta, pois você precisará de alguns de seus valores na próxima seção.

Adicione as variáveis de configuração da Auth0 à Express

🛠 Crie um arquivo .env para o servidor da API no diretório auth0-express-js-sample:

touch .env

🛠 Preencha este arquivo auth0-express-js-sample/.env da seguinte forma:

SERVER_PORT=6060
CLIENT_ORIGIN_URL=http://localhost:4040
AUTH0_AUDIENCE=
AUTH0_DOMAIN=

🛠 Volte para a página da API no dashboard da Auth0 e siga estas etapas para obter a Auth0 Audience:

Obtenha a Auth0 Audience para configurar uma API

  1. 🛠 Clique na guia "Settings".
  2. 🛠 Localize o campo "Identifier" e copie seu valor.
  3. 🛠 Cole o valor "Identifier" como o valor de AUTH0_AUDIENCE no arquivo .env.

Agora, siga estas etapas para obter o valor do Auth0 Domain:

Obtenha o Auth0 Domain para configurar uma API

  1. 🛠 Clique na aba "Test".
  2. 🛠 Localize a seção chamada "Asking Auth0 for tokens from my application".
  3. 🛠 Clique na guia cURL para mostrar uma requisição POST de exemplo.
  4. 🛠 Copie seu domínio Auth0, que faz parte do valor do parâmetro --url: nome-do-tenant.regiao.auth0.com.
  5. 🛠 Cole o valor do domínio Auth0 como o valor de AUTH0_DOMAIN no arquivo .env.
Dicas para obter o Auth0 Domain
  • O Auth0 Domain é a substring entre o protocolo, https:// e o caminho /oauth/token.
  • O Auth0 Domain segue este padrão: nome-do-tenant.regiao.auth0.com.
  • O subdomínio da região (au, us ou eu) é opcional. Alguns domínios Auth0 não o possuem.
  • Clique na imagem acima, por favor, se tiver alguma dúvida de como obter o valor do Auth0 Domain.

🛠 Com os valores de configuração .env definidos, rode o servidor da API executando o seguinte comando:

npm start

Configure o Angular para se conectar com a Express API

🛠 Volte para o diretório do projeto auth0-angular-sample que armazena sua aplicação Angular.

🛠 Localize o arquivo auth_config.json e adicione seus valores audience e server URL a ele:

{
  "domain": "YOUR_AUTH0_DOMAIN",
  "clientId": "YOUR_AUTH0_CLIENT_ID",
  "audience": "https://express.sample",
  "serverUrl": "http://localhost:6060"
}

🛠 O valor de audience é o mesmo que AUTH0_AUDIENCE que você colocou no arquivo auth0-express-js-sample/.env.

🛠 Localize o arquivo src/environments/environment.ts e atualize-o dessa forma:

// src/environments/environment.ts

import { domain, clientId, audience, serverUrl } from '../../auth_config.json';

export const environment = {
  production: false,
  auth: {
    domain,
    clientId,
    redirectUri: window.location.origin,
    audience,
  },
  dev: {
    serverUrl,
  },
};

Sua aplicação Angular precisa passar um token de acesso ao chamar uma API de destino para acessar recursos protegidos.

O SDK da Auth0 para Angular fornece um HttpInjector que anexa automaticamente tokens de acesso a requisições de saída ao usar o módulo integrado do Angular HttpClient. No entanto, você deve configurar o injetor para saber a quais requisições ele precisa anexar os tokens de acesso.

🛠 Comece importando o token HTTP_INTERCEPTORS e o AuthHttpInterceptor exatamente acima da definição @NgModule no arquivo src/app/app.module.ts:

// src/app/app.module.ts

// Other imports...

import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthHttpInterceptor } from '@auth0/auth0-angular';

@NgModule({...})
export class AppModule {}

Você está importanto AuthHttpInterceptor de @auth0/auth0-angular junto de HTTP_INTERCEPTORS do @angular/common/http. HTTP_INTERCEPTORS é um multi-provedor de tokens que representa o vetor de objetos HttpInterceptor registrados.

🛠 Depois, adicione a propriedade providers ao objeto de configuração do AppModule da seguinte forma para registrar o injetor AuthHttpInterceptor como provedor:

// src/app/app.module.ts

// All imports...

@NgModule({
  declarations: [...],
  imports: [...],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthHttpInterceptor,
      multi: true,
    },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

Isso completa a ligação necessária para conectar o AuthHttpInterceptor ao seu ciclo de requisições da sua aplicação Angular.

Agora, você precisa informar ao SDK a quais requisições anexar tokens de acesso configurando AuthModule.forRoot(). Com base nessa configuração, o Angular corresponderá à URL de qualquer requisição que você fizer usando HttpClient em uma lista de URLs permitidas.

Se houver uma correspondência, o Angular anexa um token de acesso ao cabeçalho de autorização da requisição. Você pode usar uma string ou uma expressão regular para a correspondência de URL. Por enquanto, você permitirá que o Angular anexe um token de acesso às requisições feitas para http://localhost:6060/api/messages/protected-message.

🛠 Atualize a configuração do AuthModule presente no módulo imports do AppModule assim:

// src/app/app.module.ts

// All imports...

@NgModule({
  declarations: [...],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    FontAwesomeModule,
    // 👇 update AuthModule
    AuthModule.forRoot({
      ...env.auth,
      httpInterceptor: {
        allowedList: [`${env.dev.serverUrl}/api/messages/protected-message`],
      },
    }),
  ],
  providers: [...],
  bootstrap: [...],
})
export class AppModule {}

Suponha que você faça uma chamada HTTP usando HttpClient e não haja correspondência para essa URL no AuthHttpInterceptor. Nesse caso, o Angular ignora o interceptor e faz a chamada sem um token anexado no cabeçalho Authorization.

Observação: explicitar sobre quais requisições de API exigem um token de acesso no cabeçalho de autorização evita que seus tokens sejam anexados a requisições feitas a um destinatário não intencional, o que é um sério problema de segurança. Esses destinatários podem usar esse token para chamar a API como se fosse sua aplicação.

Por fim, faça sua chamada de API usando o HttpClient, que está disponível, pois o HttpClientModule já foi importado no projeto inicial.

🛠 Atualize src/app/pages/external-api/external-api.component.ts da seguinte forma:

// src/app/pages/external-api/external-api.component.ts

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { environment as env } from '../../../environments/environment';

interface Message {
  message: string;
}

@Component({
  selector: 'app-external-api',
  templateUrl: './external-api.component.html',
})
export class ExternalApiComponent implements OnInit {
  message: string = null;

  constructor(private http: HttpClient) {}

  ngOnInit(): void {}

  callApi(): void {
    this.http
      .get(`${env.dev.serverUrl}/api/messages/public-message`)
      .subscribe((result: Message) => {
        this.message = result.message;
      });
  }

  callSecureApi(): void {
    this.http
      .get(`${env.dev.serverUrl}/api/messages/protected-message`)
      .subscribe((result: Message) => {
        this.message = result.message;
      });
  }
}

⚠️ Verifique se a URL que você está chamando usando HttpClient corresponde à regra que você tem em sua configuração httpInterceptor. Cuidado com as barras à direita.

🛠 Atualize src/app/pages/external-api/external-api.component.html assim:

<!--src/app/pages/external-api/external-api.component.html-->

<div>
  <h1>External API</h1>
  <p>
    Use these buttons to call an external API. The protected API call has an
    access token in its authorization header. The API server will validate the
    access token using the Auth0 Audience value.
  </p>
  <div
    class="btn-group mt-5"
    role="group"
    aria-label="External API Requests Examples"
  >
    <button (click)="callApi()" type="button" class="btn btn-primary">
      Get Public Message
    </button>
    <button (click)="callSecureApi()" type="button" class="btn btn-primary">
      Get Protected Message
    </button>
  </div>

  <div *ngIf="message" class="mt-5">
    <h6 class="muted">Result</h6>
    <div class="container-fluid">
      <div class="row">
        <code class="col-12 text-light bg-dark p-4"> {{ message }} </code>
      </div>
    </div>
  </div>
</div>

O que está acontecendo agora no componente ExternalApi?

Você adiciona um método callApi() que executa uma requisição de API pública e um método callSecureApi() que executa uma requisição de API segura. A implementação de cada método parece a mesma. No entanto, por baixo dos panos, o Angular encontra uma correspondência para ${env.dev.apiUrl}/api/messages/protected-message na allowedList do AuthHttpInterceptor.

Em seguida, o Angular usa o SDK da Auth0 para obter um token de acesso de Auth0 e anexa esse token de acesso como uma credencial do tipo bearer no cabeçalho de autorização da requisição.

Em seguida, você atribui o resultado da requisição bem-sucedida a this.message, que é renderizada na interface do usuário usando uma caixa de código.

Por que o valor de audiência da Auth0 é o mesmo para as duas aplicações?

Auth0 usa o valor da propriedade audience para determinar qual servidor de recursos (API) o usuário está autorizando sua aplicação Angular a acessar.

As ações que sua aplicação Angular podem executar na API dependem dos escopos contidos no seu token de acesso, que você define como o valor de uma propriedade de escopo nas tokenOptions de uma entrada de httpInterceptor.allowedList.

Lembra daquela tela que você viu quando fez login pela primeira vez com Auth0 pedindo permissão para acessar as informações de seu perfil? Sua aplicação Angular fará a requisição da autorização do usuário para acessar os escopos requisitados e o usuário aprovará ou negará a requisição. Essa tela é conhecida como caixa de diálogo de consentimento. Você pode ter visto algo semelhante ao compartilhar seus contatos ou fotos de uma plataforma de mídia social com uma aplicação de terceiros.

Quando você não passa uma propriedade scope para tokenOptions, o SDK da Auth0 para Angular usa por padrão os escopos do OpenID Connect: openid profile email.

  • openid: Este escopo informa ao servidor de autorização Auth0 que o cliente está fazendo uma requisição OpenID Connect (OIDC) para verificar a identidade do usuário. OpenID Connect é um protocolo de autenticação.
  • profile: este valor de escopo requer acesso às informações de perfil padrão do usuário, como name, nickname e picture.
  • email: este valor de escopo requer acesso às informações de email e email_verified.

Os detalhes dos escopos do OpenID Connect vão para o ID token.

No caso das suas APIs, você pode definir escopos de API personalizados para implementar o controle de acesso. Você os identificará nas chamadas que suas aplicações clientes fazem para essa API. Auth0 inclui escopos de API no token de acesso como o valor de declaração de scope.

Os conceitos sobre escopos ou permissões de API são melhor abordados em um tutorial de API Auth0, como "Use TypeScript to Create a Secure API with Node.js and Express: Role-Based Access Control".

Sua requisição de login anterior não incluía um parâmetro de audiência. Como tal, o Angular SDK não tem um token de acesso armazenado na memória.

Você não deve armazenar tokens em localStorage. Por quê?

Armazenar tokens no armazenamento local do navegador fornece persistência nas atualizações de página e guias do navegador. No entanto, se um invasor puder rodar JavaScript na aplicação de página única (Single Page Application ou SPA em inglês) usando um ataque de script entre sites (XSS), ele poderá recuperar os tokens armazenados no local storage.

Uma vulnerabilidade que leva a um ataque XSS bem-sucedido pode estar no código-fonte da SPA ou em qualquer código JavaScript de terceiros incluído no SPA, como Bootstrap, jQuery ou Google Analytics.

🛠 Efetue logout e login novamente para obter um novo token de acesso do Auth0 que inclui as informações de audiência.

🛠 Visite http://localhost:4040/external-api e clique em qualquer um dos botões na página da External API para testar as respostas.

Obtenha a mensagem pública:

The API doesn't require an access token to share this message.

Obtenha a mensagem protegida:

The API successfully validated your access token.

Conclusão

Você implementou a autenticação de usuário no Angular para identificar seus usuários, obter informações de perfil de usuário e controlar o conteúdo que seus usuários podem acessar. Você também aprendeu como fazer chamadas para API seguras entre um cliente e um servidor de uma pilha protegida com Auth0.

Este tutorial mostrou o caso de uso de autenticação mais comum para uma aplicação Angular: login e logout simples. No entanto, a Auth0 é uma plataforma extensível e flexível que pode te ajudar a chegar mais longe. Se você tiver um caso de uso mais complexo, verifique os Auth0 Architecture Scenarios para saber mais sobre os cenários de arquitetura típicos que identificamos ao trabalhar com clientes na implementação da Auth0.

Num próximo tutorial, vamos cobrir padrões e ferramentas de autenticação avançados, como usar um pop-up em vez de um redirecionamento para fazer login de usuários, adicionar permissões a ID tokens, usar metadados para aprimorar perfis de usuário e muito mais.

Deixe-me saber nos comentários abaixo se você gostou deste tutorial. Obrigado por ler este post e fica de olho para mais posts como esse.

  • Twitter icon
  • LinkedIn icon
  • Faceboook icon