Основное внимание в этом руководстве уделяется тому, чтобы помочь разработчикам узнать, как защитить приложение Angular путем реализации аутентификации пользователя. Вы усовершенствуете стартовое приложение Angular, чтобы отработать следующие концепции безопасности:

  • Добавьте пользователя для входа и выхода.
  • Получить информацию профиля пользователя.
  • Защитите маршруты приложений.
  • Вызов API с защищенными конечными точками.

В этом руководстве для защиты приложений Angular используется Auth0 Angular SDK. SDK абстрагирует множество деталей реализации аутентификации, чтобы помочь вам следовать лучшим практикам безопасности, используя идиоматический подход Angular при написании меньшего количества кода. Не нужно быть экспертом по OAuth 2.0 или OpenID Connect, чтобы понять, как защитить стек своего веб-приложения.

⏰⚡️ Если у вас мало времени, просмотрите Быстрый запуск Auth0 Angular.

Получить стартовое приложение

Ищите эмодзи 🛠️️, если хотите пролистать контент, сосредоточившись на этапах сборки.

Мы создали стартовый проект с использованием Angular CLI, чтобы помочь вам изучить концепции безопасности Angular на практике. В начальном проекте используется настраиваемая тема Bootstrap для стилизации и макета приложения, чтобы вы могли сосредоточиться на создании и подключении компонентов Angular.

🛠 Таким образом, для начала клонируйте репозиторий auth0-angular-sample в его ветку starter:

git clone -b starter [email protected]:auth0-blog/auth0-angular-sample.git

🛠 После клонирования репо сделайте auth0-angular-sample своим текущим каталогом:

🛠 Приступите к установке зависимостей проекта Angular:

🛠 Наконец, запустите приложение Angular:

Подключите Angular к Auth0

Зачем использовать Auth0 вместо создания собственной аутентификации пользователя с нуля?

В прошлом мудрые люди предупреждали: Не выкручивай собственную криптовалюту. Сегодня мудрые люди советуют: Вам не нужно создавать собственную аутентификацию.

Создание комплексной системы аутентификации и авторизации с нуля - сложная задача. Auth0 - это платформа Идентификация как услуга (IDaaS), которая позволяет централизовать аутентификацию пользователей и авторизацию API для всех ваших приложений, чтобы уменьшить эту сложность.

Auth0 предлагает мощные функции безопасности прямо из коробки. Настраиваемая страница входа в систему, вход через социальные сети, Многофакторная аутентификация (MFA) и расширенное управление пользователями позволяют начать работу в рекордно короткие сроки. Возможно, наиболее важной функцией является обнаружение аномалий, которое помогает бороться с атаками на учетные данные.

В Auth0 атаки с заполнением учетных данных составляют в среднем почти половину всех попыток входа в систему с использованием нашей платформы. Подробнее об этом векторе критической атаки читайте в статье Атаки с заполнением учетных данных: что это такое и как с ними бороться.

Как работает Auth0?

  • Вы начинаете с интеграции Auth0 с вашим приложением Angular.
  • Когда вашим пользователям необходимо войти в систему, ваше приложение Angular запускает событие аутентификации, которое оно обрабатывает, перенаправляя их на настраиваемую страницу входа Auth0.
  • После успешного входа в систему Auth0 перенаправляет их обратно в приложение Angular, возвращая токены с их аутентификацией и информацией о пользователе.
  • Кроме того, вы можете защитить свои API с помощью Auth0, чтобы вы могли использовать токен доступа для выполнения запроса от вашего приложения Angular к вашим защищенным конечным точкам API.

Насколько легко начать работу?

Очень просто! Просто выполните следующие действия:

Настроить приложение Auth0

🛠 Если вы еще этого не сделали, зарегистрируйте бесплатную учетную запись Auth0, которая предлагает вам:

В процессе регистрации вы создаете нечто, называемое Auth0 Tenant, которое представляет собой контейнер, который Auth0 использует для изолированного хранения конфигурации вашей службы идентификации и ваших пользователей. Ни один другой клиент Auth0 не может заглянуть в ваш клиент или получить к нему доступ.

🛠 После входа в систему Auth0 перенесет вас в Панель управления, где вы можете управлять своими службами идентификации и настраивать их. В меню левой боковой панели нажмите Приложения.

🛠 Затем нажмите кнопку «Создать приложение». Откроется модальное окно с формой, в которой можно указать имя приложения и выбрать его тип.

Single Page Web Applications

🛠 Нажмите кнопку «Создать», чтобы завершить процесс. Страница вашего приложения Auth0 загружается.

На следующем шаге вы узнаете, как помочь Angular и Auth0 обмениваться данными, используя данные конфигурации с этой страницы - пока не закрывайте ее.

Какая связь между клиентами Auth0 и приложениями Auth0?

Допустим, у вас есть приложение Angular для обмена фотографиями под названием Angulogram. Затем вы должны создать клиента Auth0 с именем angulogram.

Теперь предположим, что Angulogram доступен на трех платформах: в Интернете как одностраничное приложение и собственное мобильное приложение для Android и iOS. Если для каждой платформы требуется аутентификация, вам необходимо создать три приложения Auth0, чтобы предоставить продукту все необходимое для аутентификации пользователей через эту платформу.

Создайте коммуникационный мост между Angular и Auth0

При использовании Auth0 нет необходимости создавать формы входа. Auth0 предлагает страницу Универсальный вход, чтобы уменьшить накладные расходы на добавление и управление аутентификацией.

Важно отметить, что форма, предоставленная Auth0 (Auth0 Universal Login), снижает риск перечисления имени пользователя и пароля. Auth0 Universal Login правильно реализует сообщения об ошибках аутентификации в соответствии с рекомендациями OWASP (Open Web Application Security Project): скажите достаточно, чтобы помочь пользователю, который входит в систему, но не говорите слишком много, чтобы помочь злоумышленнику, пытающемуся взломать.

Как работает Универсальный вход?

Ваше приложение Angular будет перенаправлять пользователей на Auth0 всякий раз, когда они запускают запрос аутентификации. Auth0 представит им страницу универсального входа. После входа в систему Auth0 перенаправит их в ваше приложение. Чтобы это перенаправление происходило безопасно, вы должны указать в своих Настройках приложения Auth0 URL-адреса, на которые Auth0 может перенаправлять пользователей после их аутентификации.

🛠 Поэтому щелкните вкладку «Настройки» на странице приложения Auth0 и введите следующие значения:

🛠 Разрешенный URL-адрес обратного вызова

После того, как ваши пользователи успешно вошли в систему, Auth0 может перенаправить их только на любой из URL-адресов, перечисленных здесь.

🛠 Разрешенный URL выхода

После выхода ваших пользователей Auth0 может перенаправить их только на любой из URL-адресов, которые вы здесь укажете.

🛠 Разрешенное веб-происхождение

Используя Auth0 Angular SDK, ваше приложение Angular будет делать внутренние запросы к URL-адресу Auth0 для обработки запросов аутентификации. Таким образом, вам необходимо добавить исходный URL-адрес своего приложения Angular, чтобы избежать проблем Cross-Origin Resource Sharing (CORS).

🛠 Прокрутите вниз и нажмите кнопку «Сохранить изменения».

🛠 Пока не закрывайте эту страницу, поскольку вам понадобится некоторая информация о ней в следующем разделе.

Добавьте переменные конфигурации Auth0 в Angular

На странице настроек приложения Auth0 вам потребуются значения домена Auth0 и идентификатора клиента, чтобы ваше приложение Angular могло использовать только что созданный коммуникационный мост:

Что такое домен Auth0 и идентификатор клиента Auth0?
Домен

Auth0 попросил выбрать имя для вашего клиента, когда вы создали новую учетную запись Auth0. Это имя арендатора, добавленное с помощью auth0.com, является вашим доменом Auth0. Как только вы добавите в него протокол https://, вы получите базовый URL-адрес, который ваше приложение Angular может использовать для перенаправления пользователей для входа в систему и доступа к API Auth0:

Auth0 также поддерживает пользовательские домены, чтобы вы могли поддерживать свой бренд в процессе аутентификации.

Идентификатор клиента

Auth0 назначает идентификатор клиента каждому приложению, которое вы создаете на панели инструментов Auth0. Идентификатор клиента - это буквенно-цифровая строка, которая однозначно идентифицирует ваше приложение в вашем клиенте Auth0 (например, q8fij2iug0CmgPLfTfG1tZGdTQyGaTUA). Вы не можете изменить это значение. Вы будете использовать идентификатор клиента, чтобы идентифицировать приложение Auth0, к которому должен подключиться Auth0 Angular SDK.

Предупреждение. Еще одна важная информация, представленная в Настройках, - это Секрет клиента. Этот секрет защищает ваши ресурсы, предоставляя токены запрашивающим только в том случае, если они авторизованы. Думайте об этом как о пароле вашего приложения, который должен постоянно храниться в секрете. Если кто-либо получит доступ к вашему Client Secret, он сможет выдать себя за ваше приложение и получить доступ к защищенным ресурсам.

🛠 Откройте начальный проект Angular, auth0-angular-sample, и создайте файл auth_config.json в каталоге проекта:

🛠 Заполните auth_config.json следующим образом:

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

🛠 Вернитесь на страницу приложения Auth0. Выполните следующие действия, чтобы получить значения domain и clientId:

  1. 🛠 Перейдите на вкладку «Настройки», если вы еще этого не сделали.
  2. 🛠 Используйте значение «Домен» из «Настройки» как значение domain в auth_config.json.
  3. 🛠 Используйте значение «Идентификатор клиента» из «Настройки» как значение clientId в auth_config.json.

Эти переменные позволяют вашему приложению Angular идентифицировать себя как авторизованную сторону для взаимодействия с сервером аутентификации Auth0 для выполнения процесса аутентификации. Вы сопоставляете свое приложение Angular с приложением Auth0.

Чтобы использовать эти переменные в приложении Angular, вы воспользуетесь модулем Angular environment.

🛠 Замените содержимое src/environments/environment.ts следующим:

// src/environments/environment.ts import { domain, clientId } from '../../auth_config.json'; export const environment = { production: false, auth: { domain, clientId, redirectUri: window.location.origin, }, };

Как можно импортировать файлы JSON в модуль Angular? В стартовом проекте есть tsconfig.base.json файл, который устанавливает resolveJsonModule на true, что позволяет импортировать и извлекать типы из .json файлов.

Набор подключения Auth0 и Angular

Вы завершили настройку службы аутентификации, которую может использовать ваше приложение Angular. Все, что вам осталось, это продолжить создание начального проекта на протяжении всего этого руководства, добавляя компоненты и функции безопасности.

Не стесняйтесь углубиться в Документацию Auth0, чтобы узнать больше о том, как Auth0 помогает вам сэкономить время на внедрении и управлении идентификацией.

Настройте Angular SDK для Auth0

🛠 Вам необходимо выполнить следующие действия, чтобы интегрировать Auth0 Angular SDK с вашим приложением Angular.

Установите Auth0 Angular SDK

🛠 Выполните следующую команду:

ng add @auth0/auth0-angular

Angular SDK для Auth0 предоставляет несколько методов, переменных и типов, которые помогут вам идиоматически интегрировать Auth0 с вашим Angular-приложением, включая модуль аутентификации и службу.

Зарегистрируйте и настройте модуль аутентификации

SDK экспортирует модуль с компонентами и службами, необходимыми для аутентификации пользователя. Импортируйте этот модуль в AppModule, чтобы получить к нему доступ через структуру внедрения зависимостей Angular.

🛠 Импортируйте AuthModule и environment прямо над определением @NgModule в src/app/app.module.ts следующим образом:

// src/app/app.module.ts // Other imports... import { AuthModule } from '@auth0/auth0-angular'; import { environment as env } from '../environments/environment'; @NgModule({...}) export class AppModule {}

🛠 Затем добавьте AuthModule в импорт AppModule и инициализируйте его:

// 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 {}

Вы используете шаблон forRoot() для настройки AuthModule, который принимает объект со свойствами domain и clientId. Вы создаете этот объект конфигурации, расширяя объект env.auth.

Аутентификация пользователей - это механизм, позволяющий отслеживать, кто обращается к вашему приложению, и контролировать их действия. Например, вы можете запретить пользователям, которые не вошли в систему, получить доступ к частям вашего приложения. В этом случае Auth0 может выступать в роли вашего вышибалы приложения.

Вышибала - это человек, нанятый ночным клубом или аналогичным заведением для предотвращения проникновения нарушителей спокойствия или вытеснения их из помещения. Безопасность Angular не слишком отличается от безопасности ночного клуба.

Если пользователи хотят войти в защищенный маршрут из вашего приложения, Auth0 остановит их и попросит предоставить свои учетные данные. Если Auth0 может проверить, кто они и что они должны туда войти, Auth0 пропустит их. В противном случае Auth0 вернет их обратно на маршрут общедоступного приложения.

Теперь важно повторить, что процесс аутентификации не будет происходить на уровне вашего приложения. Ваше приложение Angular перенаправит ваших пользователей на страницу Универсальный вход Auth0, где Auth0 запрашивает учетные данные и перенаправляет пользователя обратно в ваше приложение с результатом процесса аутентификации.

Angular SDK для Auth0 настроен. Вы готовы создать компоненты для реализации потока аутентификации в следующем разделе.

Добавить аутентификацию пользователя

Angular SDK для Auth0 предоставляет вам методы для запуска событий аутентификации в компонентах Angular: вход, выход и регистрация.

Создать кнопку входа

🛠 Создайте LoginButtonComponent в каталоге src/components/ с помощью Angular CLI:

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

🛠 Поместите файл login-button.component.ts в каталог src/app/components/login-button/ следующим образом:

// 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(); } }

🛠 Затем поместите файл шаблона login-button.component.html в каталог src/app/components/login-button/ следующим образом:

В определении LoginButtonComponent auth.loginWithRedirect() - это метод, представленный AuthService. Этот метод предлагает пользователям пройти аутентификацию и подтвердить согласие, что означает авторизацию вашего приложения Angular для доступа к определенным данным от имени пользователя. В вашем текущем контексте это означает, что ваше приложение Angular перенаправляет пользователя на страницу универсального входа Auth0 для выполнения процесса аутентификации. Вы увидите это в действии в следующих разделах.

Вы можете настроить вход в систему, передав объект конфигурации в качестве аргумента функции loginWithRedirect(). Например, вы можете передать параметры loginWithRedirect() для перенаправления пользователей на страницу универсального входа Auth0, оптимизированную для регистрации в вашем приложении Angular. См. RedirectLoginOptions для получения дополнительных сведений об этих параметрах.

Создать кнопку регистрации

Вы можете заставить пользователей попадать прямо на страницу регистрации вместо страницы входа, добавив свойство screen_hint к объекту конфигурации auth.loginWithRedirect():

{ screen_hint: "signup", }

🛠 Создайте SignupButtonComponent в каталоге src/components/ с помощью Angular CLI:

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

🛠 Заполните файл signup-button.component.ts в src/app/components/signup-button/ следующим образом:

// 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' }); } }

🛠 Заполните файл шаблона signup-button.component.html в src/app/components/signup-button/ следующим образом:

Функция регистрации требует, чтобы вы включили Auth0 New Universal Login Experience в вашем арендаторе.

🛠 Откройте Раздел универсального входа на панели инструментов Auth0 и выберите Новый в подразделе Опыт.

🛠 Прокрутите вниз и нажмите кнопку «Сохранить изменения».

Разница между LoginButtonComponent и SignupButtonComponent пользовательским интерфейсом станет более очевидной, когда вы интегрируете эти компоненты в свое приложение Angular и увидите их в действии. Вы сделаете это в следующих разделах.

Создать кнопку выхода

🛠 Создайте LogoutButtonComponent в каталоге src/components/ следующим образом:

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

🛠 Заполните logout-button.component.ts файл в src/app/components/logout-button/ этим кодом:

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

Вы определяете logout() метод, который запускает событие выхода. Вы передаете ему необязательный объект конфигурации, чтобы сообщить Auth0, куда вести пользователей после выхода из системы.

🛠 Затем поместите файл шаблона logout-button.component.html в каталог src/app/components/logout-button/ следующим образом:

Метод auth.logout(), предоставляемый AuthService, очищает сеанс приложения и перенаправляет на конечную точку Auth0 /v2/logout, чтобы очистить сеанс Auth0. Как и в случае с методами входа в систему, вы можете передать объект конфигурации в logout(), чтобы определить параметры для вызова /v2/logout. Этот процесс практически незаметен для пользователя. См. LogoutOptions для получения более подробной информации.

Здесь вы добавляете свойство returnTo к объекту конфигурации, чтобы указать URL-адрес, по которому Auth0 должен перенаправлять ваших пользователей после выхода из системы. Прямо сейчас вы работаете локально, и разрешенные URL-адреса для выхода из системы вашего приложения Auth0 указывают на http://localhost:4040.

Однако, если вы развертывали свое приложение Angular в производственной среде, вам необходимо добавить производственный URL-адрес выхода из системы в список «Разрешенные URL-адреса выхода» и убедиться, что Auth0 перенаправляет ваших пользователей на этот производственный URL-адрес, а не localhost . Установка returnTo на this.doc.location.origin сделает именно это.

Компоненты Angular не имеют прямого доступа к объекту document. Однако вы можете @Inject константу как зависимость от AuthenticationButtonComponent. this.doc - это то же самое, что документ DOM в браузере. this.doc.location возвращает объект, свойство которого является источником вашего приложения.

Подробнее о том, как работает выход из системы в Auth0.

Объедините кнопки входа и выхода

Давайте объединим LoginButtonComponent и LogoutButtonComponent в один компонент, у которого есть логика, чтобы решить, какую кнопку отображать в зависимости от статуса аутентификации пользователя.

🛠 Создайте AuthenticationButtonComponent в каталоге src/app/components/:

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

Вы используете сокращенные обозначения g (generate) и c (component), чтобы сделать команду короче.

🛠 Поместите файл authentication-button.component.ts в каталог src/app/components/authentication-button/ следующим образом:

// 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 {} }

🛠 Поместите файл authentication-button.component.html в каталог src/app/components/authentication-button/ следующим образом:

<!-- 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>

Начнем с понимания того, что происходит в шаблоне. auth.isAuthenticated$ - это Observable, предоставляемый AuthService, который выдает логическое значение. Его значение равно true, если Auth0 аутентифицировал пользователя, и false, если нет.

Важно отметить, что под капотом auth.isAuthenticated$ начинает выдавать значения только после того, как Auth0 Angular SDK завершит загрузку. Когда AuthService.isLoading$ излучает false, тогда auth.isAuthenticated$ излучает свое значение. Этот конвейер операций помогает предотвратить ложные срабатывания в отношении статуса аутентификации пользователя. Это также вызывает небольшую задержку в рендеринге AuthenticationButtonComponent, но вы скоро это исправите.

Использование этой AuthenticationButtonComponent оболочки компонента дает некоторые преимущества:

Вы можете создавать гибкие интерфейсы. AuthenticationButtonComponent служит переключателем «вход / выход», который вы можете разместить в любом месте, где вам нужна эта функция переключателя. Однако у вас по-прежнему есть отдельные LoginButtonComponent и LogoutButtonComponent для случаев, когда вам нужна их функциональность по отдельности. Например, у вас может быть кнопка выхода на странице, которую могут видеть только аутентифицированные пользователи.

Вы можете создавать расширяемые интерфейсы. Вы можете легко поменять LoginButtonComponent на SignupButtonComponent в AuthenticationButtonComponent, чтобы создать переключатель «регистрация / выход». Вы также можете заключить переключатель «зарегистрироваться / выйти» в NewAuthenticationButtonComponent, если хотите.

Вы можете создавать декларативные интерфейсы. Используя AuthenticationButton, вы можете добавить в NavBarComponent функции входа и выхода, например, не задумываясь о деталях реализации того, как работает переключатель аутентификации.

🛠 Имея это в виду, создайте AuthNavComponent в каталоге src/components/:

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

🛠 Поместите файл auth-nav.component.html в каталог src/app/components/auth-nav/ следующим образом:

🛠 Наконец, откройте файл шаблона nav-bar.component.html в каталоге src/app/components/nav-bar/ и обновите его следующим образом:

Имея различные типы подкомпонентов навигации, вы можете расширять каждую панель навигации по своему усмотрению без повторного открытия и изменения MainNavComponent.

🛠 Попробуйте войти в систему. Ваше приложение Angular перенаправит вас на страницу универсального входа Auth0. Вы можете использовать форму для входа в систему с именем пользователя и паролем или с помощью провайдера социальной идентификации, такого как Google. Обратите внимание, что эта страница входа также дает вам возможность зарегистрироваться.

Эксперимент: используйте SignupButtonComponent

Поменяйте местами LoginButtonComponent на компонент SignupButtonComponent в шаблоне AuthenticationButtonComponent.

Когда вы нажимаете кнопку «Зарегистрироваться», вы попадаете на страницу с оптимизированным языком, чтобы побудить вас зарегистрироваться в приложении Angular.

Попробуйте это!

По завершении этого эксперимента замените SignupButtonComponent на LoginButtonComponent, чтобы продолжить работу с остальной частью этого руководства.

Вы можете настроить внешний вид страниц нового универсального входа. Вы также можете переопределить любой текст в New Experience, используя Text Customization API.

Обратите внимание, что когда вы завершаете вход и Auth0 перенаправляет вас в приложение Angular, в пользовательском интерфейсе мигает пустой экран.

Пользовательский интерфейс мигает, потому что ваше приложение Angular загружает свои службы. Во время загрузки Angular не знает, аутентифицировал ли Auth0 пользователя. Ваше приложение будет знать статус аутентификации пользователя после загрузки Auth0 Angular SDK.

🛠 Откройте src/app/app.component.ts и обновите его следующим образом:

// 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) {} }

🛠 Откройте src/app/app.component.html и обновите так:

Пока SDK загружается, рендерит LoadingComponent с классной анимацией.

Получение информации о пользователе

После успешного входа пользователя в систему Auth0 отправляет токен идентификатора вашему приложению Angular. Системы аутентификации, такие как Auth0, используют токены ID в аутентификации на основе токенов для кэширования информации профиля пользователя и предоставления ее клиентскому приложению. Кэширование токенов ID может способствовать повышению производительности и отзывчивости вашего приложения Angular.

Вы можете использовать данные из токена ID для персонализации пользовательского интерфейса вашего приложения Angular. Angular SDK для Auth0 декодирует токен идентификатора и передает свои данные через auth.user$ Observable, предоставленный AuthService. Некоторая информация идентификатора токена включает имя, псевдоним, изображение и адрес электронной почты вошедшего в систему пользователя.

Как можно использовать токен идентификатора для создания страницы профиля для своих пользователей?

🛠 Обновите ProfileComponent в src/app/pages/profile/profile.component.ts следующим образом:

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

🛠 Обновите шаблон ProfileComponent в src/app/pages/profile/profile.component.html следующим образом:

<!--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>

Что происходит в компоненте ProfileComponent?

ngOnInit() - лучшее место для инициализации данных для компонента Angular. Таким образом, вы подписываетесь на this.auth.user$ Observable в ProfileComponent. Как только this.auth.user$ генерирует объект профиля пользователя, вы используете JSON.stringify для форматирования объекта и назначения его this.profileJson. В свою очередь, вы используете директиву *ngIf для визуализации блока кода с объектом JSON профиля пользователя на основе значения profileJson.

Таким образом, вы должны защитить маршрут, который отображает этот компонент, http://localhost:4040/profile. Вы узнаете, как это сделать, в следующем разделе.

Защита маршрутов

Из всех разделов этого руководства этот самый простой в реализации благодаря надежности Angular Router. Angular SDK для Auth0 предоставляет файл, который можно использовать для защиты маршрутов.

🛠 Откройте src/app/app-routing.module.ts и обновите его следующим образом:

// 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 {}

🛠 Теперь вы можете проверить, что эти два пути, /profile и /external-api, требуют от пользователей аутентификации, прежде чем они смогут получить к ним доступ. Выйдите из системы и попробуйте получить доступ к вкладке Профиль или Внешний API. Если это сработает, Angular перенаправит вас на вход с Auth0.

Средства защиты на стороне клиента улучшают взаимодействие с пользователем вашего приложения Angular, а не его безопасность.

В статье Security StackExchange Конор Манконе объясняет, что средства защиты на стороне сервера предназначены для защиты данных, а средства защиты на стороне клиента - для улучшения взаимодействия с пользователем.

Основные выводы из его ответа:

  • Вы не можете полагаться на ограничения на стороне клиента, такие как средства навигации и защищенные маршруты, для защиты конфиденциальной информации.
  • Ваш сервер не должен возвращать какие-либо данные, к которым пользователь не должен иметь доступа.
  • Возвращать все пользовательские данные с сервера и позволять интерфейсной структуре решать, что отображать, а что скрывать на основе статуса аутентификации пользователя, является неправильным подходом.
  • Любой желающий может открыть инструменты разработчика браузера и проверить сетевые запросы, чтобы просмотреть все данные.
  • Использование средств навигации помогает улучшить взаимодействие с пользователем, а не безопасность пользователя.
  • Без охраны пользователь, который не вошел в систему, может зайти на страницу с закрытой информацией и увидеть ошибку, например «Доступ запрещен».
  • С помощью средств защиты, соответствующих разрешениям сервера, вы можете не допустить, чтобы пользователи видели ошибки, не позволяя им посещать страницу с ограниченным доступом.

Вызов API

Этот раздел посвящен тому, чтобы показать вам, как получить токен доступа в вашем приложении Angular и как использовать его для выполнения вызовов API к защищенным конечным точкам API.

Когда вы используете Auth0, вы делегируете процесс аутентификации централизованной службе. Auth0 предоставляет вам функциональные возможности для входа и выхода пользователей из вашего приложения Angular. Однако вашему приложению может потребоваться доступ к защищенным ресурсам через API.

Вы также можете защитить API с помощью Auth0. Auth0 предлагает несколько быстрых запусков API Auth0, чтобы помочь вам интегрировать Auth0 с вашей серверной платформой.

Когда вы используете Auth0 для защиты вашего API, вы также делегируете процесс авторизации централизованной службе, которая гарантирует, что только утвержденные клиентские приложения могут получить доступ к защищенным ресурсам от имени пользователя.

Как сделать безопасные вызовы API из Angular?

Ваше приложение Angular аутентифицирует пользователя и получает токен доступа от Auth0. Затем приложение может передать этот токен доступа вашему API в качестве учетных данных. В свою очередь, ваш API может использовать библиотеки Auth0 для проверки токена доступа, который он получает от вызывающего приложения, и выдачи ответа с желаемыми данными.

Вместо того, чтобы создавать API с нуля для тестирования потоков аутентификации и авторизации между клиентом и сервером, вы будете использовать демонстрационный API Express, который я подготовил для вас.

Получить демонстрацию Express API

🛠 Откройте новое окно терминала и клонируйте репозиторий auth0-express-js-sample где-нибудь в вашей системе. Убедитесь, что вы клонируете его вне каталога проекта Angular.

git clone [email protected]:auth0-blog/auth0-express-js-sample.git

🛠 После клонирования репозитория сделайте каталог auth0-express-js-sample текущим каталогом:

cd auth0-express-js-sample

🛠 Установите зависимости проекта Node.js:

Подключите Express API к Auth0

Создайте коммуникационный мост между Express и Auth0

Этот процесс аналогичен тому, как вы соединили Angular с Auth0.

🛠 Перейдите в раздел API-интерфейсы на панели инструментов Auth0 и нажмите кнопку Создать API.

🛠 Затем в форме, которую показывает Auth0:

Идентификаторы - это уникальные строки, которые помогают Auth0 различать ваши API. Мы рекомендуем использовать URL-адреса для предсказуемого создания уникальных идентификаторов; однако Auth0 никогда не вызывает эти URL-адреса.

🛠 Установив эти значения, нажмите кнопку «Создать». Не закрывайте эту страницу, поскольку в следующем разделе вам понадобятся некоторые ее значения.

Добавьте переменные конфигурации Auth0 в Express

🛠 Создайте файл .env для сервера API в каталоге auth0-express-js-sample:

🛠 Заполните этот auth0-express-js-sample/.env файл следующим образом:

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

🛠 Вернитесь на страницу API Auth0 и выполните следующие действия, чтобы получить аудиторию Auth0:

  1. 🛠 Откройте вкладку «Настройки».
  2. 🛠 Найдите поле «Идентификатор» и скопируйте его значение.
  3. 🛠 Вставьте значение «Идентификатор» как значение AUTH0_AUDIENCE в .env.

Теперь выполните следующие действия, чтобы получить значение домена Auth0:

  1. 🛠 Перейдите на вкладку «Тест».
  2. 🛠 Найдите раздел под названием «Запрос Auth0 для токенов из моего приложения».
  3. 🛠 Щелкните вкладку cURL, чтобы отобразить фиктивный POST запрос.
  4. 🛠 Скопируйте свой домен Auth0, который является частью значения параметра --url: tenant-name.region.auth0.com.
  5. 🛠 Вставьте значение домена Auth0 как значение AUTH0_DOMAIN в .env.

Советы по получению домена Auth0

  • Домен Auth0 - это подстрока между протоколом https:// и путем /oauth/token.
  • Домен Auth0 следует этому шаблону: tenant-name.region.auth0.com.
  • Поддомен region (au, us или eu) указывать необязательно. В некоторых доменах Auth0 его нет.
  • Нажмите на изображение выше, если у вас есть сомнения относительно того, как получить значение домена Auth0.

🛠 Установив значения конфигурации .env, запустите сервер API, введя следующую команду:

Настройте Angular для подключения к Express API

🛠 Вернитесь в каталог проекта auth0-angular-sample, в котором хранится ваше приложение Angular.

🛠 Найдите файл auth_config.json и добавьте к нему аудиторию и значение URL-адреса сервера:

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

🛠 Найдите файл src/environments/environment.ts и обновите его следующим образом:

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

Ваше приложение Angular должно передавать токен доступа, когда оно вызывает целевой API для доступа к защищенным ресурсам.

Angular SDK для Auth0 предоставляет HttpInjector, который автоматически присоединяет токены доступа к исходящим запросам при использовании встроенного модуля Angular HttpClient. Однако вы должны настроить инжектор, чтобы знать, к каким запросам он должен присоединять токены доступа.

🛠 Начните с импорта токена HTTP_INTERCEPTORS и AuthHttpInterceptor прямо над определением @NgModule в файле 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 {}

Вы импортируете AuthHttpInterceptor из @auth0/auth0-angular вместе с HTTP_INTERCEPTORS из @angular/common/http. HTTP_INTERCEPTORS - токен с несколькими провайдерами, который представляет собой массив зарегистрированных HttpInterceptor объектов.

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

Это завершает проводку, необходимую для подключения AuthHttpInterceptor к вашему циклу запросов приложения Angular.

Теперь вам нужно указать SDK, к каким запросам прикреплять токены доступа, путем дальнейшей настройки AuthModule.forRoot(). На основе этой конфигурации Angular будет сопоставлять URL-адрес любого запроса, который вы делаете с помощью HttpClient, с разрешенным списком URL-адресов.

Если есть совпадение, Angular прикрепляет токен доступа к заголовку авторизации запроса. Вы можете использовать строку или регулярное выражение для сопоставления URL-адресов. На данный момент вы позволите Angular прикреплять токен доступа к запросам, которые он отправляет к http://localhost:6060/api/messages/protected-message.

// 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 {}

Примечание. Четкое указание того, для каких запросов API требуется маркер доступа в заголовке авторизации, предотвращает привязку ваших маркеров к запросам, отправленным непреднамеренному получателю, что является серьезной проблемой безопасности. Затем эти получатели могли использовать этот токен для вызова API, как если бы это было ваше приложение.

Наконец, вызовите API, используя HttpClient, который доступен, поскольку HttpClientModule уже был импортирован в начальный проект.

🛠 Обновите src/app/pages/external-api/external-api.component.ts следующим образом:

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

⚠️ Убедитесь, что URL-адрес, который вы вызываете с помощью HttpClient, соответствует правилу, заданному вами в httpInterceptor конфигурации. Следите за косой чертой в конце.

🛠 Обновите src/app/pages/external-api/external-api.component.html следующим образом:

<!--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>

Что сейчас происходит в компоненте ExternalApi?

Вы добавляете callApi() метод, который выполняет запрос общедоступного API, и метод callSecureApi(), выполняющий безопасный запрос API. Реализация каждого метода выглядит одинаково. Однако под капотом Angular находит соответствие для ${env.dev.apiUrl}/api/messages/protected-message в allowedList из AuthHttpInterceptor.

Затем Angular использует Auth0 SDK для получения токена доступа от Auth0 и прикрепляет этот токен доступа в качестве учетных данных носителя в заголовке авторизации запроса.

Затем вы назначаете результат успешного запроса this.message, который вы отображаете в пользовательском интерфейсе с помощью поля кода.

Почему значение Auth0 Audience одинаково для клиентских и серверных приложений?

Auth0 использует значение свойства audience, чтобы определить, к какому серверу ресурсов (API) пользователь разрешает доступ вашему приложению Angular.

Действия, которые ваше приложение Angular может выполнять с API, зависят от областей, которые содержит ваш токен доступа. Вы можете определить значение области в tokenOptions записи httpInterceptor.allowedList.

Помните тот экран, который вы видели, когда впервые вошли в систему с Auth0, спрашивая разрешения на доступ к информации вашего профиля? Ваше приложение Angular запросит у пользователя авторизацию для доступа к запрошенным областям, и пользователь утвердит или отклонит запрос. Этот экран известен как диалог согласия. Возможно, вы видели это раньше, используя GitHub, Google или Facebook для входа в систему или когда делитесь своими контактами электронной почты с третьим лицом.

Если вы не передаете свойство scope в tokenOptions, Angular SDK по умолчанию использует Области OpenID Connect: openid profile email.

  • openid: Эта область сообщает серверу авторизации Auth0, что клиент делает запрос OpenID Connect (OIDC) для проверки личности пользователя. OpenID Connect - это протокол аутентификации.
  • profile: это значение области запрашивает доступ к информации профиля пользователя по умолчанию, такой как name, nickname и picture.
  • email: это значение области запрашивает доступ к информации email и email_verified.

Подробная информация об областях OpenID Connect содержится в ID Token.

В случае ваших API вы определите собственные области API для реализации контроля доступа и идентифицируете их в вызовах, которые ваши клиентские приложения делают для этого API. Auth0 включает области API в токен доступа в качестве scope утверждения.

Концепции, касающиеся областей действия или разрешений API, лучше освещены в учебном пособии по API Auth0, например Использование TypeScript для создания безопасного API с помощью Node.js и Express: управление доступом на основе ролей.

Ваш предыдущий запрос на вход не включал параметр аудитории. Таким образом, Angular SDK не имеет токена доступа, хранящегося в памяти.

Не следует хранить токены в localStorage. Почему?

Хранение токенов в локальном хранилище браузера обеспечивает постоянство при обновлении страниц и на вкладках браузера. Однако, если злоумышленник может запустить JavaScript в одностраничном приложении (SPA), используя атаку межсайтового скриптинга (XSS), он может получить токены, хранящиеся в локальном хранилище. .

Уязвимость, ведущая к успешной атаке XSS, может быть либо в исходном коде SPA, либо в любом стороннем коде JavaScript, включенном в SPA, например Bootstrap, jQuery или Google Analytics.

🛠 Выйдите из системы и войдите снова, чтобы получить новый токен доступа от Auth0, который включает информацию об аудитории.

🛠 Посетите http://localhost:4040/external-api и нажмите любую кнопку на странице внешнего API, чтобы проверить ответы.

Получить общедоступное сообщение:

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

Получите защищенное сообщение:

The API successfully validated your access token.

Вывод

Вы реализовали аутентификацию пользователей в Angular, чтобы идентифицировать ваших пользователей, получать информацию профиля пользователя и контролировать контент, к которому ваши пользователи могут получить доступ. Вы также узнали, как выполнять безопасные вызовы API между клиентом и сервером стека, защищенного с помощью Auth0.

В этом руководстве описан наиболее распространенный вариант использования аутентификации для приложения Angular: простой вход и выход из системы. Однако Auth0 - это расширяемая и гибкая платформа, которая может помочь вам достичь еще большего. Если у вас более сложный вариант использования, ознакомьтесь с Сценариями архитектуры Auth0, чтобы узнать больше о типичных сценариях архитектуры, которые мы определили при работе с клиентами над внедрением Auth0.

В следующем руководстве мы рассмотрим расширенные шаблоны и инструменты аутентификации, такие как использование всплывающего окна вместо перенаправления для входа пользователей, добавление разрешений к токенам идентификатора, использование метаданных для улучшения профилей пользователей и многое другое.

Дайте мне знать в комментариях ниже, как вам понравился этот урок. Спасибо, что прочитали этот пост, и следите за новостями, пожалуйста.

Первоначально опубликовано на https://www.coodingdessign.com.