diff --git a/README.md b/README.md index 8785473..3889d8b 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ If you have any questions or suggestions - [Example with Supabase using Ory Kratos and Ory Oathkeeper](https://github.com/ory/examples/tree/master/kratos-keto-oathkeeper-supabase) - [Example with Supabase using Ory Cloud](https://github.com/ory/examples/tree/master/supabase-ory-cloud) - [Example API Gateway with Kong using Ory Kratos & Ory Oathkeeper](https://github.com/ory/examples/tree/master/kratos-oathkeeper-kong) +- [Example implementation of OAuth 2.0 Authorization Code Flow with PKCE using Ory Hydra(Spring, Angular and Docker Compose)](https://github.com/ory/examples/tree/master/ory-hydra-oauth2.0-spring-angular-docker-compose) ### Configuration Examples diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/.env b/ory-hydra-oauth2.0-spring-angular-docker-compose/.env new file mode 100644 index 0000000..e452dda --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/.env @@ -0,0 +1,28 @@ +# ------------------!!!EDIT!!!---------------- + +IP_AUTHORIZATION_SERVER=192.168.0.101 +IP_RESOURCE_SERVER=192.168.0.104 +IP_CLIENT_READONLY=192.168.0.102 +IP_CLIENT_WRITE_AND_READ=192.168.0.103 + +DNS_AUTHORIZATION_SERVER=authorization-server.com +DNS_RESOURCE_SERVER=resource-server.com +DNS_CLIENT_READONLY=client-readonly.com +DNS_CLIENT_WRITE_AND_READ=client-write-and-read.com + +USER_AUTHORIZATION_SERVER=someuser +USER_RESOURCE_SERVER=someuser +USER_CLIENT_READONLY=someuser +USER_CLIENT_WRITE_AND_READ=someuser + +USER_DATA_POSTGRESQL_PASSWORD=cklGS7BNMT6Io9Yd8FKzg4ZmWLXjQnA24JbXNHbG + +HYDRA_POSTGRESQL_PASSWORD=7pj3gK8arVwk6A1BbUD2XysfIYmKdEk0DL8BMRNx + +HYDRA_SECRETS_COOKIE=OT9Z8I2NcBp01rP4FwQG7JEt6nuXeJ0BDpf4Bjwc +HYDRA_SECRETS_SYSTEM=cIsKS4VzJCDpXlwm2PNTb7v60GHh1iEYZPiiPpRS + +HYDRA_INTROSPECT_USER=user_introspect +HYDRA_INTROSPECT_PASSWORD=hUq7Mw3fr4lFjnHQtoJucgDdAV58NbAOvuGN2OfB + +# ------------------------------------------ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/.gitignore b/ory-hydra-oauth2.0-spring-angular-docker-compose/.gitignore new file mode 100644 index 0000000..de90ece --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/.gitignore @@ -0,0 +1,4 @@ +.$ory-hydra-oauth2-example.drawio.bkp +.$ory-hydra-oauth2-example.drawio.dtmp + +ca_* \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/README.md b/ory-hydra-oauth2.0-spring-angular-docker-compose/README.md new file mode 100644 index 0000000..3884434 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/README.md @@ -0,0 +1,221 @@ +# **ORY Hydra OAuth2 Example** + +[Русский язык](README_RUS.md) + +## Description + +Example implementation of [OAuth 2.0 Authorization Code with PKCE](https://www.ory.sh/docs/oauth2-oidc/authorization-code-flow) using [ORY Hydra](https://www.ory.sh/hydra/). + +Login Flow and Consent Flow are implemented using Spring boot 2(Java 11, WebFlux), Angular 17, PostgreSQL 15. + +Everything has been tested: Karma, JUnit 5, Testcontainers. + +## How to build example Login Flow Wrapper, Consent Flow Wrapper, OAuth 2.0 Client(s), OAuth 2.0 Resource Server + +Let's take Login Flow and Consent Flow Wrapper as an example. + +### Build Frontend + +path: + +```bash +cd ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/ +``` + +If you need to perform testing (Karma), then: + +```bash +cd ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/ && \ +npm i && \ +bash test_and_report.bash +``` + +If you need to find out the code coverage of tests, then you need to open the following through the browser: + +```text +ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/coverage/authorization-frontend/index.html +``` + +To build a docker image, do the following (ATTENTION! You must install pack. The instructions are inside build_image.bash): + +```bash + +# install pack +# https://buildpacks.io/docs/tools/pack/#linux-script-install +# (curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.29.0/pack-v0.29.0-linux.tgz" | sudo tar -C /usr/local/bin/ --no-same-owner -xzv pack) + +cd ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/ && \ +bash build_image.bash +``` + +### Build Backend + +path: + +```bash +cd ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/ +``` + +If you need to perform testing (JUnit 5), then (**ATTENTION!** Since Testcontainers is used for testing, you must have rights to run docker (sudo usermod -aG docker \[user\])): + +```bash +cd ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/ && \ +./gradlew -v && \ +bash test_and_report.bash +``` + +If you need to find out the code coverage of tests (JaCoCo), then you need to open the following in the browser: + +```text +ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/build/reports/jacoco/test/html/index.html +``` + +To build a docker image, do the following: + +```bash +cd ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/ && \ +bash build_image.bash +``` + +### Proxy + +#### Docker (Debian) + +```bash +mkdir -p /etc/systemd/system/docker.service.d + +# set proxy +cat > /etc/systemd/system/docker.service.d/http-proxy.conf <<-EOF +[Service] +Environment="HTTP_PROXY=http://proxyuser:proxypass@192.168.20.4:8822/" +Environment="HTTPS_PROXY=http://proxyuser:proxypass@192.168.20.4:8822/" +Environment="NO_PROXY=localhost,127.0.0.1" +EOF + +# restart docker +sudo systemctl daemon-reload +sudo systemctl restart docker + +# check +systemctl show --property=Environment docker + +``` + +#### APT (Docker image) + +If you need to specify a proxy server (see ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/build_image.bash or ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/build_image.bash), then uncomment HTTP_PROXY (HTTPS_PROXY or/and NO_PROXY) and edit HTTP_PROXY ( HTTPS_PROXY or/and NO_PROXY) + +```bash + #!/bin/bash + +export HTTP_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +export HTTPS_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +export NO_PROXY="localhost,127.0.0.1" + +REPO_IMAGE="chistousov" +... + +``` + +## Example + +![ory-hydra-oauth2-example](ory-hydra-oauth2-example.png) + +4 Debian: +| Type | DNS, Hostname | IP | +| ------------- | ------------- | ------------- | +| OAuth 2.0 Authorization Server | authorization-server.com | 192.168.0.101 | +| OAuth 2.0 Client (Readonly) | client-readonly.com | 192.168.0.102 | +| OAuth 2.0 Client (Write and read) | client-write-and-read.com | 192.168.0.103 | +| OAuth 2.0 Resource Server | resource-server.com | 192.168.0.104 | + +Requires openssl, jq (apt install jq), htpasswd (apt install apache2-utils), envsubst to run start.bash. + +Required on remote servers: Docker, Docker Compose (V3) + +We organize ssh and coordinate the settings with the .env file +```bash +# ------------------!!!EDIT!!!---------------- + +IP_AUTHORIZATION_SERVER=192.168.0.101 +IP_RESOURCE_SERVER=192.168.0.104 +IP_CLIENT_READONLY=192.168.0.102 +IP_CLIENT_WRITE_AND_READ=192.168.0.103 + +DNS_AUTHORIZATION_SERVER=authorization-server.com +DNS_RESOURCE_SERVER=resource-server.com +DNS_CLIENT_READONLY=client-readonly.com +DNS_CLIENT_WRITE_AND_READ=client-write-and-read.com + +USER_AUTHORIZATION_SERVER=someuser +USER_RESOURCE_SERVER=someuser +USER_CLIENT_READONLY=someuser +USER_CLIENT_WRITE_AND_READ=someuser + +USER_DATA_POSTGRESQL_PASSWORD=cklGS7BNMT6Io9Yd8FKzg4ZmWLXjQnA24JbXNHbG + +HYDRA_POSTGRESQL_PASSWORD=7pj3gK8arVwk6A1BbUD2XysfIYmKdEk0DL8BMRNx + +HYDRA_SECRETS_COOKIE=OT9Z8I2NcBp01rP4FwQG7JEt6nuXeJ0BDpf4Bjwc +HYDRA_SECRETS_SYSTEM=cIsKS4VzJCDpXlwm2PNTb7v60GHh1iEYZPiiPpRS + +HYDRA_INTROSPECT_USER=user_introspect +HYDRA_INTROSPECT_PASSWORD=hUq7Mw3fr4lFjnHQtoJucgDdAV58NbAOvuGN2OfB + +# ------------------------------------------ + +``` + +To find out the IP address you can run ***ip a***. + +**WARNING!** Users USER_AUTHORIZATION_SERVER, USER_RESOURCE_SERVER, USER_CLIENT_READONLY and USER_CLIENT_WRITE_AND_READ must be allowed to run docker compose (V3) (sudo usermod -aG docker \[user\]). + +We run the script to configure four servers: +```bash +bash start.bash +``` + +### Checking + +On the Resource Owner computer, DESCRIBE IP ADDRESSES IN THE FILE /etc/hosts (Linux). + +```bash +# Let's say +#DNS_AUTHORIZATION_SERVER=authorization-server.com +#DNS_RESOURCE_SERVER=resource-server.com +#DNS_CLIENT_READONLY=client-readonly.com +#DNS_CLIENT_WRITE_AND_READ=client-write-and-read.com + +echo '192.168.0.101 authorization-server.com' >> /etc/hosts +echo '192.168.0.102 client-readonly.com' >> /etc/hosts +echo '192.168.0.103 client-write-and-read.com' >> /etc/hosts +echo '192.168.0.104 resource-server.com' >> /etc/hosts +# check +ping authorization-server.com +ping client-readonly.com +ping client-write-and-read.com +ping resource-server.com +``` + +0. Register Resource Owner . +1. Go to . +2. Since the user is not logged in, the user is redirected to (Login Flow, authentication), then to (Consent Flow, authorization). +3. Next, the user is taken back to . +4. To receive data, OAuth 2.0 Client (Readonly) contacts with an access token. + + +### Stop + +Stopping containers that save data (volume). + +```bash +bash stop.bash +``` + +### Stop and clean + +We stop the containers and delete all data (volume). + +```bash +bash stop_and_clean.bash +``` \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/README_RUS.md b/ory-hydra-oauth2.0-spring-angular-docker-compose/README_RUS.md new file mode 100644 index 0000000..15a672a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/README_RUS.md @@ -0,0 +1,218 @@ +# **ORY Hydra OAuth2 Example** + +## Описание + +Пример реализации [OAuth 2.0 Authorization Code with PKCE](https://www.ory.sh/docs/oauth2-oidc/authorization-code-flow) с использованием [ORY Hydra](https://www.ory.sh/hydra/). + +Login Flow и Consent Flow реализованы с использованием Spring boot 2(Java 11, WebFlux), Angular 17, PostgreSQL 15. + +Все оттестировано: Karma, JUnit 5, Testcontainers. + +## Как собрать example Login Flow Wrapper, Consent Flow Wrapper, OAuth 2.0 Client(s), OAuth 2.0 Resource Server + +В качестве примера возьмем Login Flow and Consent Flow Wrapper. + +### Build Frontend + +path: + +```bash +cd ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/ +``` + +Если необходимо выполнить тестирование (Karma), то: + +```bash +cd ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/ && \ +npm i && \ +bash test_and_report.bash +``` + +Если необходимо узнать покрытие кода тестами, то необходимо открыть через браузер следующее: + +```text +ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/coverage/authorization-frontend/index.html +``` + +Чтобы собрать docker image выполним следующее (ВНИМАНИЕ! Необходимо установить pack. Инструкция внутри build_image.bash): + +```bash + +# install pack +# https://buildpacks.io/docs/tools/pack/#linux-script-install +# (curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.29.0/pack-v0.29.0-linux.tgz" | sudo tar -C /usr/local/bin/ --no-same-owner -xzv pack) + +cd ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/ && \ +bash build_image.bash +``` + +### Build Backend + +path: + +```bash +cd ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/ +``` + +Если необходимо выполнить тестирование (JUnit 5), то (**ВНИМАНИЕ!** Т.к. для тестирования используется Testcontainers, то необходимы права на запуск docker (sudo usermod -aG docker \[user\])): + +```bash +cd ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/ && \ +./gradlew -v && \ +bash test_and_report.bash +``` + +Если необходимо узнать покрытие кода тестами (JaCoCo), то необходимо открыть через браузер следующее: + +```text +ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/build/reports/jacoco/test/html/index.html +``` + +Чтобы собрать docker image выполним следующее: + +```bash +cd ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/ && \ +bash build_image.bash +``` + +### Proxy + +#### Docker (Debian) + +```bash +mkdir -p /etc/systemd/system/docker.service.d + +# set proxy +cat > /etc/systemd/system/docker.service.d/http-proxy.conf <<-EOF +[Service] +Environment="HTTP_PROXY=http://proxyuser:proxypass@192.168.20.4:8822/" +Environment="HTTPS_PROXY=http://proxyuser:proxypass@192.168.20.4:8822/" +Environment="NO_PROXY=localhost,127.0.0.1" +EOF + +# restart docker +sudo systemctl daemon-reload +sudo systemctl restart docker + +# check +systemctl show --property=Environment docker + +``` + +#### APT (Docker image) + +Если необходимо указать прокси сервер (see ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/build_image.bash or ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/build_image.bash), то раскомментируйте HTTP_PROXY (HTTPS_PROXY or/and NO_PROXY) и отредактируйте HTTP_PROXY (HTTPS_PROXY or/and NO_PROXY) + +```bash + #!/bin/bash + +export HTTP_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +export HTTPS_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +export NO_PROXY="localhost,127.0.0.1" + +REPO_IMAGE="chistousov" +... + +``` + +## Пример + +![ory-hydra-oauth2-example](ory-hydra-oauth2-example.png) + +4 Debian: +| Type | DNS, Hostname | IP | +| ------------- | ------------- | ------------- | +| OAuth 2.0 Authorization Server | authorization-server.com | 192.168.0.101 | +| OAuth 2.0 Client (Readonly) | client-readonly.com | 192.168.0.102 | +| OAuth 2.0 Client (Write and read) | client-write-and-read.com | 192.168.0.103 | +| OAuth 2.0 Resource Server | resource-server.com | 192.168.0.104 | + +Требуется openssl, jq (apt install jq), htpasswd (apt install apache2-utils), envsubst для выполнения start.bash. + +Требуется на удаленных серверах: Docker, Docker Compose (V3) + +Организовываем ssh и согласуем настройки с .env файлом +```bash +# ------------------!!!EDIT!!!---------------- + +IP_AUTHORIZATION_SERVER=192.168.0.101 +IP_RESOURCE_SERVER=192.168.0.104 +IP_CLIENT_READONLY=192.168.0.102 +IP_CLIENT_WRITE_AND_READ=192.168.0.103 + +DNS_AUTHORIZATION_SERVER=authorization-server.com +DNS_RESOURCE_SERVER=resource-server.com +DNS_CLIENT_READONLY=client-readonly.com +DNS_CLIENT_WRITE_AND_READ=client-write-and-read.com + +USER_AUTHORIZATION_SERVER=someuser +USER_RESOURCE_SERVER=someuser +USER_CLIENT_READONLY=someuser +USER_CLIENT_WRITE_AND_READ=someuser + +USER_DATA_POSTGRESQL_PASSWORD=cklGS7BNMT6Io9Yd8FKzg4ZmWLXjQnA24JbXNHbG + +HYDRA_POSTGRESQL_PASSWORD=7pj3gK8arVwk6A1BbUD2XysfIYmKdEk0DL8BMRNx + +HYDRA_SECRETS_COOKIE=OT9Z8I2NcBp01rP4FwQG7JEt6nuXeJ0BDpf4Bjwc +HYDRA_SECRETS_SYSTEM=cIsKS4VzJCDpXlwm2PNTb7v60GHh1iEYZPiiPpRS + +HYDRA_INTROSPECT_USER=user_introspect +HYDRA_INTROSPECT_PASSWORD=hUq7Mw3fr4lFjnHQtoJucgDdAV58NbAOvuGN2OfB + +# ------------------------------------------ + +``` + +Чтоб узнать ip адрес можно выполнить ***ip a***. + +**ВНИМАНИЕ!** Пользователи USER_AUTHORIZATION_SERVER, USER_RESOURCE_SERVER, USER_CLIENT_READONLY и USER_CLIENT_WRITE_AND_READ должны иметь право запускать docker compose (V3) (sudo usermod -aG docker \[user\]). + +Запускаем скрипт для конфигурирования четырех серверов: +```bash +bash start.bash +``` + +### Проверяем + +На компьютере Resource Owner ОПИСЫВАЕМ IP АДРЕСА В ФАЙЛЕ /etc/hosts (Linux). + +```bash +# Допустим +#DNS_AUTHORIZATION_SERVER=authorization-server.com +#DNS_RESOURCE_SERVER=resource-server.com +#DNS_CLIENT_READONLY=client-readonly.com +#DNS_CLIENT_WRITE_AND_READ=client-write-and-read.com + +echo '192.168.0.101 authorization-server.com' >> /etc/hosts +echo '192.168.0.102 client-readonly.com' >> /etc/hosts +echo '192.168.0.103 client-write-and-read.com' >> /etc/hosts +echo '192.168.0.104 resource-server.com' >> /etc/hosts +# check +ping authorization-server.com +ping client-readonly.com +ping client-write-and-read.com +ping resource-server.com +``` + +0. Регистрируем Resource Owner . +1. Заходим на . +2. Так как пользоавтель не вошел в систему, то пользователя перебрасывает на (Login Flow, аутентификация), потом на (Consent Flow, авторизация). +3. Далее пользователь попадает обратно на . +4. Для получения данных OAuth 2.0 Client (Readonly) обращается на с access token. + +### Stop + +Останавливаем контейнеры с сохранением данных (volume). + +```bash +bash stop.bash +``` + +### Stop and clean + +Останавливаем контейнеры и удаляем все данные(volume). + +```bash +bash stop_and_clean.bash +``` \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory-hydra-oauth2-example.drawio b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory-hydra-oauth2-example.drawio new file mode 100644 index 0000000..f846f65 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory-hydra-oauth2-example.drawio @@ -0,0 +1,474 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory-hydra-oauth2-example.png b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory-hydra-oauth2-example.png new file mode 100644 index 0000000..95386a6 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory-hydra-oauth2-example.png differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/.gitignore b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/.gitignore new file mode 100644 index 0000000..c7fc79b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/.gitignore @@ -0,0 +1,13 @@ +.env + +**/ca.key +**/ca.crt +**/ca.srl + +nginx/cert +nginx/confs/htpasswd_introspect + +authorization-server.com.truststore.p12 + +.env_readonly +.env_read_and_write \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/.editorconfig b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/.editorconfig new file mode 100644 index 0000000..59d9a3a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/.gitignore b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/build.gradle b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/build.gradle new file mode 100644 index 0000000..f402dd4 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/build.gradle @@ -0,0 +1,98 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '2.7.13' + id 'io.spring.dependency-management' version '1.0.15.RELEASE' + id 'jacoco' +} + +ext { + httpproxy = "$System.env.HTTP_PROXY" ?: "" + httpsproxy = "$System.env.HTTPS_PROXY" ?: "" + noproxy = "$System.env.NO_PROXY" ?: "" + + repoImage = "$System.env.REPO_IMAGE" ?: "" + projectName = "$System.env.PROJECT_NAME" ?: "" + versionProj = "$System.env.VERSION" ?: "" + + set('testcontainersVersion', "1.18.3") +} + +group = 'com.github.chistousov' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '11' + withSourcesJar() +} + +compileJava.options.encoding = "UTF-8" +compileTestJava.options.encoding = "UTF-8" + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +jacocoTestReport { + dependsOn test + reports { + xml.required = true + } +} + +repositories { + mavenCentral() +} + +bootBuildImage { + imageName = "${repoImage}/${projectName}:${versionProj}" + environment = [ + // "HTTP_PROXY" : httpproxy.toString(), + // "HTTPS_PROXY" : httpsproxy.toString(), + // "NO_PROXY" : noproxy.toString(), + // add health check for docker + "BP_HEALTH_CHECKER_ENABLED": "true" + ] + buildpacks = ["urn:cnb:builder:paketo-buildpacks/java", "gcr.io/paketo-buildpacks/health-checker:latest"] +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + + implementation 'org.springframework.boot:spring-boot-starter-security' + + implementation 'org.springframework.boot:spring-boot-starter-webflux' + + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'javax.validation:validation-api:2.0.1.Final' + + implementation 'org.springframework.boot:spring-boot-starter-actuator' + + compileOnly 'org.projectlombok:lombok' + + runtimeOnly 'org.postgresql:postgresql' + + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' + annotationProcessor 'org.projectlombok:lombok' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'io.projectreactor:reactor-test' + testImplementation 'org.springframework.security:spring-security-test' + testImplementation 'org.testcontainers:junit-jupiter' + testImplementation 'org.testcontainers:postgresql' + + testImplementation "org.testcontainers:mockserver:1.18.3" + testImplementation "org.mock-server:mockserver-client-java:5.15.0" +} + +dependencyManagement { + imports { + mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}" + } +} + +tasks.named('test') { + useJUnitPlatform() + finalizedBy jacocoTestReport +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/build_image.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/build_image.bash new file mode 100644 index 0000000..ce9b434 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/build_image.bash @@ -0,0 +1,20 @@ +#!/bin/bash + +# export HTTP_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +# export HTTPS_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +# export NO_PROXY="localhost,127.0.0.1" + +export REPO_IMAGE="chistousov" +export PROJECT_NAME="ory-hydra-oauth2-example-authorization-server-backend" +export VERSION="1.0.0" + +docker pull paketobuildpacks/builder-jammy-full:0.3.316 + +./gradlew clean test +./gradlew bootBuildImage --builder=paketobuildpacks/builder-jammy-full:0.3.316 + +# publish in docker hub +# docker login +# docker push $REPO_IMAGE/$PROJECT_NAME:$VERSION +# docker logout +# docker rmi $REPO_IMAGE/$PROJECT_NAME:$VERSION diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/gradle/wrapper/gradle-wrapper.jar b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..249e583 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/gradle/wrapper/gradle-wrapper.jar differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/gradle/wrapper/gradle-wrapper.properties b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..774fae8 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/gradlew b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/gradlew new file mode 100644 index 0000000..a69d9cb --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/gradlew.bat b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/gradlew.bat new file mode 100644 index 0000000..f127cfd --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/lombok.config b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/lombok.config new file mode 100644 index 0000000..8f7e8aa --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/lombok.config @@ -0,0 +1 @@ +lombok.addLombokGeneratedAnnotation = true \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/settings.gradle b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/settings.gradle new file mode 100644 index 0000000..d6018ab --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'authorization-backend' diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/AuthorizationBackendApplication.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/AuthorizationBackendApplication.java new file mode 100644 index 0000000..089ccf9 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/AuthorizationBackendApplication.java @@ -0,0 +1,16 @@ +package com.github.chistousov.authorization_backend; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.github.chistousov.authorization_backend.jacoco_ignore.ExcludeFromJacocoGeneratedReport; + +@SpringBootApplication +@ExcludeFromJacocoGeneratedReport +public class AuthorizationBackendApplication { + + public static void main(String[] args) { + SpringApplication.run(AuthorizationBackendApplication.class, args); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/JpaConfiguration.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/JpaConfiguration.java new file mode 100644 index 0000000..0b2bb07 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/JpaConfiguration.java @@ -0,0 +1,77 @@ +package com.github.chistousov.authorization_backend; + +import java.util.concurrent.Executors; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.dao.support.PersistenceExceptionTranslator; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.support.TransactionTemplate; + +import com.github.chistousov.authorization_backend.dao.exception_translators.CustomPersistenceExceptionTranslator; + +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; + +@Configuration +public class JpaConfiguration { + + @Value("${spring.datasource.hikari.maximum-pool-size}") + private int connectionPoolSize; + + /** + *

+ * To create a thread pool for an asynchronous call to stored procedures by the + * number of connection pool + *

+ * + *

+ * Для создания пула потоков для асинхроного вызова хранимым процедур по + * количеству пула подключений + *

+ * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + @Bean + public Scheduler jdbcScheduler() { + return Schedulers.fromExecutor(Executors.newFixedThreadPool(connectionPoolSize)); + } + + /** + *

+ * To manually create a transaction + *

+ * + *

+ * Для ручного создания транзакции + *

+ * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + @Bean + public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) { + return new TransactionTemplate(transactionManager); + } + + /** + *

+ * Maps errors from the database to specific exceptions + *

+ * + *

+ * Отображает ошибки с БД в определенные исключения + *

+ * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + + @Bean + public PersistenceExceptionTranslator customPersistenceExceptionTranslator() { + return new CustomPersistenceExceptionTranslator(); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/SpringSecurityConfiguration.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/SpringSecurityConfiguration.java new file mode 100644 index 0000000..a109eff --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/SpringSecurityConfiguration.java @@ -0,0 +1,30 @@ +package com.github.chistousov.authorization_backend; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.server.SecurityWebFilterChain; +import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository; + +@Configuration +@EnableWebFluxSecurity +public class SpringSecurityConfiguration { + + @Bean + public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { + http + .authorizeExchange( + ex -> ex.pathMatchers("/registration", "/login", "/consent/**", "/logout", "/actuator/health").permitAll()) + .csrf(csrf -> csrf.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())); + return http.build(); + } + + @Bean + public PasswordEncoder passwordEncoderRegister() { + return new BCryptPasswordEncoder(); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/ConsentProviderController.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/ConsentProviderController.java new file mode 100644 index 0000000..fef9fdc --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/ConsentProviderController.java @@ -0,0 +1,182 @@ +package com.github.chistousov.authorization_backend.controllers; + +import java.net.URI; +import java.util.Collections; +import java.util.List; + +import org.springframework.core.env.Environment; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.WebSession; + +import com.github.chistousov.authorization_backend.models.AcceptConsentRequestModelBuilder; +import com.github.chistousov.authorization_backend.models.ErrorModel; +import com.github.chistousov.authorization_backend.models.ErrorModelBuilder; +import com.github.chistousov.authorization_backend.models.ResponseWithRedirectModel; +import com.github.chistousov.authorization_backend.services.OryHydraService; +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; + +@RestController +@RequestMapping("consent") +@Slf4j +public class ConsentProviderController { + + private static final String CONSENT_CHALLENGE = "consent_challenge"; + + private OryHydraService oryHydraService; + + private String frontendURI; + + final Mono errorModelRejectConsent = Mono.just( + ErrorModelBuilder + .builder() + .setError("access_denied") + .setErrorDescription( + "The resource owner denied the request. ") + .setErrorHint("") + .setStatusCode(403L) + .build()); + + public ConsentProviderController(Environment env, OryHydraService oryHydraService) { + + this.oryHydraService = oryHydraService; + + this.frontendURI = env.getProperty("application.ory-hydra.frontend.consent-redirectURI"); + } + + @GetMapping + public Mono> getConsent(@RequestParam(CONSENT_CHALLENGE) String consentChallenge, + WebSession webSession) { + + return oryHydraService.consentRequestInfo(consentChallenge) + .flatMap(consentRequestInfo -> { + + // if the user has consented in before + if (consentRequestInfo.getSkip().booleanValue() + || consentRequestInfo.getClient().getSkipConsent().booleanValue()) { + + var acceptConsentRequestModel = Mono.just(AcceptConsentRequestModelBuilder.builder() + .setGrantScope(consentRequestInfo.getRequestedScope()) + .build()); + + return oryHydraService.acceptConsentRequest(consentChallenge, acceptConsentRequestModel) + // redirect browser + .map(responseWithRedirectModel -> ResponseEntity + .status(HttpStatus.FOUND) + .location(URI.create(responseWithRedirectModel.getRedirectTo())) + .build()) + .doOnEach(el -> log.info("Skip consent")); + + } + + // remember consent challenge ID + webSession.getAttributes().put(CONSENT_CHALLENGE, consentChallenge); + + return Mono.just( + ResponseEntity + .status(HttpStatus.FOUND) + .location(URI.create(frontendURI)) + .build()) + .doOnEach(el -> log.info("User gets consent page")); + }); + } + + @GetMapping("subject") + public Mono> getSubject(WebSession webSession) { + final String consentChallenge = webSession.getAttributes().get(CONSENT_CHALLENGE).toString(); + + return oryHydraService.consentRequestInfo(consentChallenge) + .map(consentRequestInfo -> ResponseEntity + .ok(consentRequestInfo.getSubject())) + .doOnTerminate(() -> log.info("get subject")); + } + + @GetMapping("client-name") + public Mono> getClientName(WebSession webSession) { + final String consentChallenge = webSession.getAttributes().get(CONSENT_CHALLENGE).toString(); + + return oryHydraService.consentRequestInfo(consentChallenge) + .map(consentRequestInfo -> { + + // client name or client Id + String clientName = consentRequestInfo.getClient().getClientName(); + + if (clientName == null || clientName.isBlank()) { + clientName = consentRequestInfo.getClient().getClientId(); + } + + return ResponseEntity + .ok(clientName); + }) + .doOnTerminate(() -> log.info("get scopes")); + } + + @GetMapping("scopes") + public Mono>> getScopes(WebSession webSession) { + final String consentChallenge = webSession.getAttributes().get(CONSENT_CHALLENGE).toString(); + + return oryHydraService.consentRequestInfo(consentChallenge) + .map(consentRequestInfo -> ResponseEntity + .ok(consentRequestInfo.getRequestedScope())) + .doOnTerminate(() -> log.info("get scopes")); + } + + @PutMapping + public Mono> postConsentConfirm( + @RequestParam(name = "is-remember", required = false, defaultValue = "false") boolean isRemember, + WebSession webSession) { + + final String consentChallenge = webSession.getAttributes().get(CONSENT_CHALLENGE).toString(); + + return oryHydraService.consentRequestInfo(consentChallenge) + .flatMap(consentRequestInfo -> { + + // requested scopes + final List scopes = Collections.unmodifiableList(consentRequestInfo.getRequestedScope()); + + // accept consent model + var acceptConsentRequestModel = Mono.just( + AcceptConsentRequestModelBuilder.builder() + .setGrantScope(scopes) + .setRemember(isRemember) + .build()); + + return oryHydraService.acceptConsentRequest(consentChallenge, acceptConsentRequestModel) + // redirect browser + .map(responseWithRedirectModel -> { + + webSession.getAttributes().remove(CONSENT_CHALLENGE); + + return ResponseEntity + .status(HttpStatus.CREATED) + .body(responseWithRedirectModel); + }); + + }); + } + + @DeleteMapping("cancel") + public Mono> deleteConsentCancel(WebSession webSession) { + + final String consentChallenge = webSession.getAttributes().get(CONSENT_CHALLENGE).toString(); + + return oryHydraService.rejectConsentRequest(consentChallenge, errorModelRejectConsent) + // redirect browser + .map(responseWithRedirectModel -> { + + webSession.getAttributes().remove(CONSENT_CHALLENGE); + + return ResponseEntity + .status(HttpStatus.CREATED) + .body(responseWithRedirectModel); + }); + + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/GlobalExceptionHandler.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/GlobalExceptionHandler.java new file mode 100644 index 0000000..e3f7682 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/GlobalExceptionHandler.java @@ -0,0 +1,108 @@ +package com.github.chistousov.authorization_backend.controllers; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.dao.DataAccessException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.support.WebExchangeBindException; + +import com.github.chistousov.authorization_backend.exceptions.IncorrectPasswordException; +import com.github.chistousov.authorization_backend.exceptions.LoginDoesNotExistException; +import com.github.chistousov.authorization_backend.exceptions.relational_database.LoginOrOrgExistException; +import com.github.chistousov.authorization_backend.jacoco_ignore.ExcludeFromJacocoGeneratedReport; + +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@ControllerAdvice +@Slf4j +@ExcludeFromJacocoGeneratedReport +public class GlobalExceptionHandler { + + private static final String TEXT_PLAIN_CHARSET_UTF_8 = "text/plain;charset=utf-8"; + private static final String CONTENT_TYPE = "Content-Type"; + + private static final String ERROR_IN_DATABASE_MESSAGE = "Database error "; + + // invalid model handler + @ExceptionHandler(WebExchangeBindException.class) + public ResponseEntity> handleException(WebExchangeBindException e) { + var errors = e.getBindingResult() + .getAllErrors() + .stream() + .map(DefaultMessageSourceResolvable::getDefaultMessage) + .collect(Collectors.toList()); + return ResponseEntity.badRequest().body(errors); + + } + + @ExceptionHandler(DataAccessException.class) + public Mono dataBaseHandler(ServerHttpRequest req, ServerHttpResponse response, DataAccessException ex) { + + response.getHeaders().set(CONTENT_TYPE, TEXT_PLAIN_CHARSET_UTF_8); + response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + + byte[] bodyResponse = ERROR_IN_DATABASE_MESSAGE.getBytes(StandardCharsets.UTF_8); + DataBuffer buffer = response.bufferFactory().wrap(bodyResponse); + + return response.writeWith(Flux.just(buffer)) + .doOnEach(el -> log.error(req.getPath().toString() + " ", ex)); + } + + @ExceptionHandler({ LoginDoesNotExistException.class, IncorrectPasswordException.class, + LoginOrOrgExistException.class }) + public Mono anyExceptionHandler(ServerHttpRequest req, ServerHttpResponse response, RuntimeException ex) { + + response.getHeaders().set(CONTENT_TYPE, TEXT_PLAIN_CHARSET_UTF_8); + response.setStatusCode(HttpStatus.BAD_REQUEST); + + Optional errorMesOptional = Optional.ofNullable(ex.getMessage()); + String errorMes = errorMesOptional.orElseGet(() -> ERROR_IN_DATABASE_MESSAGE); + + byte[] bodyResponse = errorMes.getBytes(StandardCharsets.UTF_8); + DataBuffer buffer = response.bufferFactory().wrap(bodyResponse); + + return response.writeWith(Flux.just(buffer)) + .doOnEach(el -> log.error(req.getPath().toString() + " ", ex)); + } + + @ExceptionHandler(IllegalArgumentException.class) + public Mono illegalArgumentPerfom(ServerHttpRequest req, ServerHttpResponse response, + IllegalArgumentException ex) { + + response.getHeaders().set(CONTENT_TYPE, TEXT_PLAIN_CHARSET_UTF_8); + response.setStatusCode(HttpStatus.BAD_REQUEST); + + byte[] bodyResponse = "Request received with incorrect data ".getBytes(StandardCharsets.UTF_8); + DataBuffer buffer = response.bufferFactory().wrap(bodyResponse); + + return response.writeWith(Flux.just(buffer)) + .doOnEach(el -> log.error(req.getPath().toString() + " ", ex)); + + } + + @ExceptionHandler(Exception.class) + public Mono commonHandler(ServerHttpRequest req, ServerHttpResponse response, Exception ex) { + + response.getHeaders().set(CONTENT_TYPE, TEXT_PLAIN_CHARSET_UTF_8); + response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + + byte[] bodyResponse = "Error in server ".getBytes(StandardCharsets.UTF_8); + DataBuffer buffer = response.bufferFactory().wrap(bodyResponse); + + return response.writeWith(Flux.just(buffer)) + .doOnEach(el -> log.error(req.getPath().toString() + " ", ex)); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/LoginProviderController.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/LoginProviderController.java new file mode 100644 index 0000000..6ca08aa --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/LoginProviderController.java @@ -0,0 +1,164 @@ +package com.github.chistousov.authorization_backend.controllers; + +import java.net.URI; + +import org.springframework.core.env.Environment; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.WebSession; + +import com.github.chistousov.authorization_backend.models.AcceptLoginRequestModelBuilder; +import com.github.chistousov.authorization_backend.models.ErrorModelBuilder; +import com.github.chistousov.authorization_backend.models.PostLoginModel; +import com.github.chistousov.authorization_backend.models.ResponseWithRedirectModel; +import com.github.chistousov.authorization_backend.services.OryHydraService; +import com.github.chistousov.authorization_backend.services.UserService; + +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; + +@RestController +@RequestMapping("login") +@Slf4j +public class LoginProviderController { + + private static final String NUMBER_OF_LOGIN_ATTEMPTS_ZERO = "Количество попыток входа истекло"; + private static final String NUMBER_OF_LOGIN_ATTEMPTS = "number_of_login_attempts"; + private static final String LOGIN_CHALLENGE = "login_challenge"; + + private UserService userService; + private OryHydraService oryHydraService; + + // number of login attempts + private int numberOfLoginAttempts; + + private String frontendURI; + + public LoginProviderController(Environment env, UserService userService, OryHydraService oryHydraService) { + + this.userService = userService; + this.oryHydraService = oryHydraService; + + this.numberOfLoginAttempts = Integer + .parseUnsignedInt(env.getProperty("application.ory-hydra.number-of-login-attempts", "5")); + + this.frontendURI = env.getProperty("application.ory-hydra.frontend.login-redirectURI"); + } + + @GetMapping + public Mono> getLogin(@RequestParam(LOGIN_CHALLENGE) String loginChallenge, + WebSession webSession) { + + return oryHydraService.loginRequestInfo(loginChallenge) + .flatMap(loginRequestInfo -> { + + // if the user has logged in before + if (loginRequestInfo.getSkip().booleanValue()) { + + // login validation model + var acceptLoginRequestModel = userService + .getUser(loginRequestInfo.getSubject()) + .map( + userModel -> AcceptLoginRequestModelBuilder.builder() + .setSubject(userModel.getLogin()) + .setRemember(true) + // if getIsRemember == true, then remember for a day + .setRememberFor(60L * 60L * 24L) + .build()); + + return oryHydraService.acceptLoginRequest( + loginChallenge, + acceptLoginRequestModel) + // redirect browser + .map(responseWithRedirectModel -> ResponseEntity + .status(HttpStatus.FOUND) + .location(URI.create(responseWithRedirectModel.getRedirectTo())) + .build()) + .doOnEach(el -> log.info("User already logged in")); + } + + // remember login challenge ID + webSession.getAttributes().put(LOGIN_CHALLENGE, loginChallenge); + // set the number of login attempts + webSession.getAttributes().put(NUMBER_OF_LOGIN_ATTEMPTS, String.valueOf(numberOfLoginAttempts)); + + return Mono.just( + ResponseEntity + .status(HttpStatus.FOUND) + .location(URI.create(frontendURI)) + .build()) + .doOnEach(el -> log.info("User gets login page")); + }); + } + + @PostMapping + public Mono> postLogin(@RequestBody PostLoginModel postLoginModel, + WebSession webSession) { + + // get loginChallenge + String loginChallenge = webSession.getRequiredAttribute(LOGIN_CHALLENGE).toString(); + // get the number of attempts + long numberAttempts = Long.parseUnsignedLong(webSession.getRequiredAttribute(NUMBER_OF_LOGIN_ATTEMPTS)); + + if (numberAttempts == 0L) { + var errorModel = Mono.just( + ErrorModelBuilder + .builder() + .setError("request_denied") + .setErrorDebug(NUMBER_OF_LOGIN_ATTEMPTS_ZERO) + .setErrorDescription( + "The number of login attempts has expired! Close the tab and re-login to the app.") + .setErrorHint(NUMBER_OF_LOGIN_ATTEMPTS_ZERO) + .setStatusCode(401L) + .build()); + + return oryHydraService.rejectLoginRequest( + loginChallenge, + errorModel) + // redirect browser + .map(responseWithRedirectModel -> ResponseEntity + .status(HttpStatus.CREATED) + .body(responseWithRedirectModel)) + .doOnEach(el -> log.info(NUMBER_OF_LOGIN_ATTEMPTS_ZERO)); + } + + numberAttempts--; + + webSession.getAttributes().put(NUMBER_OF_LOGIN_ATTEMPTS, String.valueOf(numberAttempts)); + + // Mono to validate user into database by password + return userService + .getUserAndCheck(postLoginModel.getLogin(), postLoginModel.getPassword()) + .flatMap(user -> { + + var acceptLoginRequestModel = Mono.just( + AcceptLoginRequestModelBuilder.builder() + .setSubject(user.getLogin()) + .setRemember(postLoginModel.getIsRemember()) + // if getIsRemember == true, then remember for a day + .setRememberFor(60L * 60L * 24L) + .build()); + + return oryHydraService.acceptLoginRequest(loginChallenge, acceptLoginRequestModel) + // redirect browser + .map(responseWithRedirectModel -> { + + webSession.getAttributes().remove(LOGIN_CHALLENGE); + webSession.getAttributes().remove(NUMBER_OF_LOGIN_ATTEMPTS); + + return ResponseEntity + .status(HttpStatus.CREATED) + .body(responseWithRedirectModel); + }); + }) + .doOnEach(el -> log.info("Login закончился успехом. Переходит на consent.")); + + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/LogoutProviderController.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/LogoutProviderController.java new file mode 100644 index 0000000..b915725 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/LogoutProviderController.java @@ -0,0 +1,99 @@ +package com.github.chistousov.authorization_backend.controllers; + +import java.net.URI; + +import org.springframework.core.env.Environment; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.WebSession; + +import com.github.chistousov.authorization_backend.models.PutLogoutModel; +import com.github.chistousov.authorization_backend.models.ResponseWithRedirectModel; +import com.github.chistousov.authorization_backend.services.OryHydraService; + +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; + +@RestController +@RequestMapping("logout") +@Slf4j +public class LogoutProviderController { + + private static final String LOGOUT_CHALLENGE = "logout_challenge"; + + private OryHydraService oryHydraService; + + private String frontendURI; + private String logoutCancelfrontendURI; + + public LogoutProviderController(Environment env, OryHydraService oryHydraService) { + + this.oryHydraService = oryHydraService; + + this.frontendURI = env.getProperty("application.ory-hydra.frontend.logout-redirectURI"); + this.logoutCancelfrontendURI = env.getProperty("application.ory-hydra.frontend.logout-cancel-redirectURI"); + } + + @GetMapping + public Mono> getLogout(@RequestParam(LOGOUT_CHALLENGE) String logoutChallenge, + WebSession webSession) { + + return oryHydraService.logoutRequestInfo(logoutChallenge) + .doOnNext(logoutRequestInfo -> log.info("Subject comes out: {}", logoutRequestInfo.getSubject())) + .flatMap(logoutRequestInfo -> { + + // remember the exit attempt ID (logoutChallenge challenge) + webSession.getAttributes().put(LOGOUT_CHALLENGE, logoutChallenge); + + return Mono.just( + ResponseEntity + .status(HttpStatus.FOUND) + .location(URI.create(frontendURI)) + .build()) + .doOnTerminate( + () -> log.info("The user receives a logout confirmation page")); + }); + + } + + @PutMapping + public Mono> putLogout(@RequestBody PutLogoutModel putLogoutModel, + WebSession webSession) { + + final String logoutChallenge = webSession.getRequiredAttribute(LOGOUT_CHALLENGE).toString(); + + if (putLogoutModel.getIsConfirmed().booleanValue()) { + return this.oryHydraService.acceptLogoutRequest(logoutChallenge) + .log() + .doOnNext(e -> log.info(e.toString())) + .flatMap( + responseWithRedirectModel -> Mono.just( + ResponseEntity + .status(HttpStatus.CREATED) + .body(responseWithRedirectModel)) + .doOnTerminate(() -> log.info("The user confirmed the withdrawal"))); + } + + return this.oryHydraService.rejectLogoutRequest(logoutChallenge) + .switchIfEmpty( + Mono.just( + ResponseWithRedirectModel.builder() + .redirectTo(logoutCancelfrontendURI) + .build())) + .flatMap( + responseWithRedirectModel -> Mono.just( + ResponseEntity + .status(HttpStatus.CREATED) + .body( + responseWithRedirectModel)) + .doOnTerminate(() -> log.info("The user canceled the withdrawal"))); + + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/registration/RegistrationController.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/registration/RegistrationController.java new file mode 100644 index 0000000..d9a27df --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/controllers/registration/RegistrationController.java @@ -0,0 +1,40 @@ +package com.github.chistousov.authorization_backend.controllers.registration; + +import javax.validation.Valid; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; + +import com.github.chistousov.authorization_backend.models.PostRegistrationModel; +import com.github.chistousov.authorization_backend.services.UserService; + +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; + +@Controller +@RequestMapping("/registration") +@Slf4j +public class RegistrationController { + + private UserService userService; + + public RegistrationController(UserService userService) { + this.userService = userService; + } + + @PostMapping + public Mono> postRegistration(@Valid @RequestBody PostRegistrationModel postRegistrationModel) { + + return this.userService + .createUser(postRegistrationModel) + .map(idUser -> ResponseEntity + .status(HttpStatus.CREATED) + .body(idUser)) + .doOnSuccess(el -> log.info("User is created. ")); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/dao/entities/User.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/dao/entities/User.java new file mode 100644 index 0000000..e040f87 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/dao/entities/User.java @@ -0,0 +1,49 @@ +package com.github.chistousov.authorization_backend.dao.entities; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@NamedStoredProcedureQuery(name = "User.createUser", procedureName = "horns_and_hooves.add_user", parameters = { + @StoredProcedureParameter(name = "login", type = String.class, mode = ParameterMode.IN), + @StoredProcedureParameter(name = "password", type = String.class, mode = ParameterMode.IN), + @StoredProcedureParameter(name = "org_name", type = String.class, mode = ParameterMode.IN), + @StoredProcedureParameter(name = "user_id", type = Long.class, mode = ParameterMode.OUT) +}) + +@NamedStoredProcedureQuery(name = "User.getUserByLogin", procedureName = "horns_and_hooves.get_login", resultClasses = { + User.class }, parameters = { + @StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = void.class), + @StoredProcedureParameter(mode = ParameterMode.IN, type = String.class) + }) + +@NoArgsConstructor +@Getter +@Builder +@EqualsAndHashCode(exclude = { "id", "password" }) +@AllArgsConstructor +@Entity +public class User { + + @Id + @Column(name = "id") + private Long id; + + @Column(name = "login") + private String login; + + @Column(name = "password") + private String password; + + public void setPassword(String password) { + this.password = password; + } + + @Column(name = "org_name") + private String orgName; + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/dao/exception_translators/CustomPersistenceExceptionTranslator.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/dao/exception_translators/CustomPersistenceExceptionTranslator.java new file mode 100644 index 0000000..d3849c9 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/dao/exception_translators/CustomPersistenceExceptionTranslator.java @@ -0,0 +1,35 @@ +package com.github.chistousov.authorization_backend.dao.exception_translators; + +import javax.persistence.PersistenceException; + +import org.hibernate.exception.ConstraintViolationException; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.support.PersistenceExceptionTranslator; +import org.springframework.lang.Nullable; + +import com.github.chistousov.authorization_backend.exceptions.relational_database.LoginOrOrgExistException; +import com.github.chistousov.authorization_backend.jacoco_ignore.ExcludeFromJacocoGeneratedReport; + +@ExcludeFromJacocoGeneratedReport +public class CustomPersistenceExceptionTranslator implements PersistenceExceptionTranslator { + + @Override + @Nullable + public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + if (ex instanceof PersistenceException + && + ((PersistenceException) ex).getCause() instanceof ConstraintViolationException) { + + var persistenceException = (PersistenceException) ex; + var constraintViolationException = (ConstraintViolationException) persistenceException.getCause(); + + String sql = constraintViolationException.getSQL(); + + if (sql.equals("horns_and_hooves.add_user")) { + return new LoginOrOrgExistException("The login or organization exists! "); + } + } + return null; + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/dao/repositories/UserRepository.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/dao/repositories/UserRepository.java new file mode 100644 index 0000000..c8b229b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/dao/repositories/UserRepository.java @@ -0,0 +1,19 @@ +package com.github.chistousov.authorization_backend.dao.repositories; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import com.github.chistousov.authorization_backend.dao.entities.User; + +@Repository +public interface UserRepository extends JpaRepository { + + @Procedure("User.createUser") + Long createUser( + @Param("login") String login, + @Param("password") String password, + @Param("org_name") String orgName); + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/exceptions/AcceptLoginRequestModelException.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/exceptions/AcceptLoginRequestModelException.java new file mode 100644 index 0000000..07685dc --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/exceptions/AcceptLoginRequestModelException.java @@ -0,0 +1,8 @@ +package com.github.chistousov.authorization_backend.exceptions; + +public class AcceptLoginRequestModelException extends RuntimeException { + + public AcceptLoginRequestModelException(String message) { + super(message); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/exceptions/IncorrectPasswordException.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/exceptions/IncorrectPasswordException.java new file mode 100644 index 0000000..1eecffe --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/exceptions/IncorrectPasswordException.java @@ -0,0 +1,8 @@ +package com.github.chistousov.authorization_backend.exceptions; + +public class IncorrectPasswordException extends RuntimeException { + + public IncorrectPasswordException(String message) { + super(message); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/exceptions/LoginDoesNotExistException.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/exceptions/LoginDoesNotExistException.java new file mode 100644 index 0000000..7e3511b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/exceptions/LoginDoesNotExistException.java @@ -0,0 +1,8 @@ +package com.github.chistousov.authorization_backend.exceptions; + +public class LoginDoesNotExistException extends RuntimeException { + + public LoginDoesNotExistException(String message) { + super(message); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/exceptions/relational_database/LoginOrOrgExistException.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/exceptions/relational_database/LoginOrOrgExistException.java new file mode 100644 index 0000000..86d8885 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/exceptions/relational_database/LoginOrOrgExistException.java @@ -0,0 +1,10 @@ +package com.github.chistousov.authorization_backend.exceptions.relational_database; + +import org.springframework.dao.DataAccessException; + +public class LoginOrOrgExistException extends DataAccessException { + + public LoginOrOrgExistException(String message) { + super(message); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/jacoco_ignore/ExcludeFromJacocoGeneratedReport.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/jacoco_ignore/ExcludeFromJacocoGeneratedReport.java new file mode 100644 index 0000000..bc970fe --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/jacoco_ignore/ExcludeFromJacocoGeneratedReport.java @@ -0,0 +1,21 @@ +package com.github.chistousov.authorization_backend.jacoco_ignore; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marking with this annotation, we ignore code coverage by JaCoCo tests for a + * class or method. + * Помечая данной аннотацией, мы игнорируем для класса или метода покрытие кода + * тестами JaCoCo + */ +@Documented +@Retention(RUNTIME) +@Target({ TYPE, METHOD }) +public @interface ExcludeFromJacocoGeneratedReport { +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/AcceptConsentRequestModel.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/AcceptConsentRequestModel.java new file mode 100644 index 0000000..0090d00 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/AcceptConsentRequestModel.java @@ -0,0 +1,31 @@ +package com.github.chistousov.authorization_backend.models; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Getter; + +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +public class AcceptConsentRequestModel { + + private Boolean remember; + + private SessionForTokenModel session; + + @JsonProperty("grant_scope") + private List grantScope; + + @JsonProperty("remember_for") + private Long rememberFor; + + public AcceptConsentRequestModel(AcceptConsentRequestModelBuilder builder) { + this.remember = builder.getRemember(); + this.session = builder.getSession(); + this.grantScope = builder.getGrantScope(); + this.rememberFor = builder.getRememberFor(); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/AcceptConsentRequestModelBuilder.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/AcceptConsentRequestModelBuilder.java new file mode 100644 index 0000000..5bca314 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/AcceptConsentRequestModelBuilder.java @@ -0,0 +1,48 @@ +package com.github.chistousov.authorization_backend.models; + +import java.util.List; + +import lombok.Getter; + +@Getter +public class AcceptConsentRequestModelBuilder { + private Boolean remember; + private SessionForTokenModel session; + private List grantScope; + private Long rememberFor; + + public static AcceptConsentRequestModelBuilder builder() { + return new AcceptConsentRequestModelBuilder(); + } + + private AcceptConsentRequestModelBuilder() { + } + + public AcceptConsentRequestModelBuilder setRemember(Boolean remember) { + this.remember = remember; + return this; + } + + public AcceptConsentRequestModelBuilder setSession(SessionForTokenModel session) { + this.session = session; + return this; + } + + public AcceptConsentRequestModelBuilder setGrantScope(List grantScope) { + this.grantScope = grantScope; + return this; + } + + public AcceptConsentRequestModelBuilder setRememberFor(Long rememberFor) { + this.rememberFor = rememberFor; + return this; + } + + public Boolean getRemember() { + return remember; + } + + public AcceptConsentRequestModel build() { + return new AcceptConsentRequestModel(this); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/AcceptLoginRequestModel.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/AcceptLoginRequestModel.java new file mode 100644 index 0000000..71c4633 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/AcceptLoginRequestModel.java @@ -0,0 +1,36 @@ +package com.github.chistousov.authorization_backend.models; + +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.chistousov.authorization_backend.exceptions.AcceptLoginRequestModelException; + +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode(of = { "subject" }) +@JsonIgnoreProperties(ignoreUnknown = true) +public class AcceptLoginRequestModel { + private String subject; + + private Boolean remember; + + @JsonProperty("remember_for") + private Long rememberFor; + + private Object context; + + public AcceptLoginRequestModel(AcceptLoginRequestModelBuilder acceptLoginRequestModelBuilder) { + if (Objects.isNull(acceptLoginRequestModelBuilder.getSubject()) + || acceptLoginRequestModelBuilder.getSubject().isBlank()) { + throw new AcceptLoginRequestModelException("Subject not set"); + } + + this.subject = acceptLoginRequestModelBuilder.getSubject(); + this.remember = acceptLoginRequestModelBuilder.getRemember(); + this.rememberFor = acceptLoginRequestModelBuilder.getRememberFor(); + this.context = acceptLoginRequestModelBuilder.getContextModel(); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/AcceptLoginRequestModelBuilder.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/AcceptLoginRequestModelBuilder.java new file mode 100644 index 0000000..53e1f04 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/AcceptLoginRequestModelBuilder.java @@ -0,0 +1,44 @@ +package com.github.chistousov.authorization_backend.models; + +import lombok.Getter; + +@Getter +public class AcceptLoginRequestModelBuilder { + + private String subject; + private Boolean remember; + private Long rememberFor; + private Object contextModel; + + public static AcceptLoginRequestModelBuilder builder() { + return new AcceptLoginRequestModelBuilder(); + } + + private AcceptLoginRequestModelBuilder() { + } + + public AcceptLoginRequestModelBuilder setRemember(Boolean remember) { + this.remember = remember; + return this; + } + + public AcceptLoginRequestModelBuilder setRememberFor(Long rememberFor) { + this.rememberFor = rememberFor; + return this; + } + + public AcceptLoginRequestModelBuilder setSubject(String subject) { + this.subject = subject; + return this; + } + + public AcceptLoginRequestModelBuilder setContextModel(Object contextModel) { + this.contextModel = contextModel; + return this; + } + + public AcceptLoginRequestModel build() { + return new AcceptLoginRequestModel(this); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/Client.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/Client.java new file mode 100644 index 0000000..11334ae --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/Client.java @@ -0,0 +1,159 @@ +package com.github.chistousov.authorization_backend.models; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class Client { + + @JsonProperty("allowed_cors_origins") + private List allowedCorsOrigins; + + private List audience; + + @JsonProperty("authorization_code_grant_access_token_lifespan") + private String authorizationCodeGrantAccessTokenLifespan; + + @JsonProperty("authorization_code_grant_id_token_lifespan") + private String authorizationCodeGrantIdTokenLifespan; + + @JsonProperty("authorization_code_grant_refresh_token_lifespan") + private String authorizationCodeGrantRefreshTokenLifespan; + + @JsonProperty("backchannel_logout_session_required") + private boolean backchannelLogoutSessionRequired; + + @JsonProperty("backchannel_logout_uri") + private String backchannelLogoutUri; + + @JsonProperty("client_credentials_grant_access_token_lifespan") + private String clientCredentialsGrantAccessTokenLifespan; + + @JsonProperty("client_id") + private String clientId; + + @JsonProperty("client_name") + private String clientName; + + @JsonProperty("client_secret") + private String clientSecret; + + @JsonProperty("client_secret_expires_at") + private int clientSecretExpiresAt; + + @JsonProperty("client_uri") + private String clientUri; + + private List contacts; + + @JsonProperty("created_at") + private String createdAt; + + @JsonProperty("frontchannel_logout_session_required") + private boolean frontchannelLogoutSessionRequired; + + @JsonProperty("frontchannel_logout_uri") + private String frontchannelLogoutUri; + + @JsonProperty("grant_types") + private List grantTypes; + + @JsonProperty("implicit_grant_access_token_lifespan") + private String implicitGrantAccessTokenLifespan; + + @JsonProperty("implicit_grant_id_token_lifespan") + private String implicitGrantIdTokenLifespan; + + private Object jwks; + + @JsonProperty("jwks_uri") + private String jwksUri; + + @JsonProperty("jwt_bearer_grant_access_token_lifespan") + private String jwtBearerGrantAccessTokenLifespan; + + @JsonProperty("logo_uri") + private String logoUri; + + private Object metadata; + + private String owner; + + @JsonProperty("password_grant_access_token_lifespan") + private String passwordGrantAccessTokenLifespan; + + @JsonProperty("password_grant_refresh_token_lifespan") + private String passwordGrantRefreshTokenLifespan; + + @JsonProperty("policy_uri") + private String policyUri; + + @JsonProperty("post_logout_redirect_uris") + private List postLogoutRedirectUris; + + @JsonProperty("redirect_uris") + private List redirectUris; + + @JsonProperty("refresh_token_grant_access_token_lifespan") + private String refreshTokenGrantAccessTokenLifespan; + + @JsonProperty("refresh_token_grant_id_token_lifespan") + private String refreshTokenGrantIdTokenLifespan; + + @JsonProperty("refresh_token_grant_refresh_token_lifespan") + private String refreshTokenGrantRefreshTokenLifespan; + + @JsonProperty("registration_access_token") + private String registrationAccessToken; + + @JsonProperty("registration_client_uri") + private String registrationClientUri; + + @JsonProperty("request_object_signing_alg") + private String requestObjectSigningAlg; + + @JsonProperty("request_uris") + private List requestUris; + + @JsonProperty("response_types") + private List responseTypes; + + private String scope; + + @JsonProperty("sector_identifier_uri") + private String sectorIdentifierUri; + + @JsonProperty("skip_consent") + private Boolean skipConsent; + + @JsonProperty("subject_type") + private String subjectType; + + @JsonProperty("token_endpoint_auth_method") + private String tokenEndpointAuthMethod; + + @JsonProperty("token_endpoint_auth_signing_alg") + private String tokenEndpointAuthSigningAlg; + + @JsonProperty("tos_uri") + private String tosUri; + + @JsonProperty("updated_at") + private String updatedAt; + + @JsonProperty("userinfo_signed_response_alg") + private String userinfoSignedResponseAlg; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/ErrorModel.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/ErrorModel.java new file mode 100644 index 0000000..705462a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/ErrorModel.java @@ -0,0 +1,33 @@ +package com.github.chistousov.authorization_backend.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Getter; + +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +public class ErrorModel { + private String error; + + @JsonProperty("error_debug") + private String errorDebug; + + @JsonProperty("error_description") + private String errorDescription; + + @JsonProperty("error_hint") + private String errorHint; + + @JsonProperty("status_code") + private Long statusCode; + + public ErrorModel(ErrorModelBuilder builder) { + this.error = builder.getError(); + this.errorDebug = builder.getErrorDebug(); + this.errorDescription = builder.getErrorDescription(); + this.errorHint = builder.getErrorHint(); + this.statusCode = builder.getStatusCode(); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/ErrorModelBuilder.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/ErrorModelBuilder.java new file mode 100644 index 0000000..be88d6b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/ErrorModelBuilder.java @@ -0,0 +1,49 @@ +package com.github.chistousov.authorization_backend.models; + +import lombok.Getter; + +@Getter +public class ErrorModelBuilder { + private String error; + private String errorDebug; + private String errorDescription; + private String errorHint; + private Long statusCode; + + public static ErrorModelBuilder builder() { + return new ErrorModelBuilder(); + } + + private ErrorModelBuilder() { + } + + public ErrorModelBuilder setError(String error) { + this.error = error; + return this; + } + + public ErrorModelBuilder setErrorDebug(String errorDebug) { + this.errorDebug = errorDebug; + return this; + } + + public ErrorModelBuilder setErrorDescription(String errorDescription) { + this.errorDescription = errorDescription; + return this; + } + + public ErrorModelBuilder setErrorHint(String errorHint) { + this.errorHint = errorHint; + return this; + } + + public ErrorModelBuilder setStatusCode(Long statusCode) { + this.statusCode = statusCode; + return this; + } + + public ErrorModel build() { + return new ErrorModel(this); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/GetConsentResponseModel.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/GetConsentResponseModel.java new file mode 100644 index 0000000..bd4ee35 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/GetConsentResponseModel.java @@ -0,0 +1,53 @@ +package com.github.chistousov.authorization_backend.models; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class GetConsentResponseModel { + + private String acr; + + private List amr; + + private String challenge; + + private Client client; + + private Object context; + + @JsonProperty("login_challenge") + private String loginChallenge; + + @JsonProperty("login_session_id") + private String loginSessionId; + + @JsonProperty("oidc_context") + private Object oidcContext; + + @JsonProperty("request_url") + private String requestUrl; + + @JsonProperty("requested_access_token_audience") + private List requestedAccessTokenAudience; + + @JsonProperty("requested_scope") + private List requestedScope; + + private Boolean skip; + + private String subject; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/GetLoginResponseModel.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/GetLoginResponseModel.java new file mode 100644 index 0000000..bfd5a99 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/GetLoginResponseModel.java @@ -0,0 +1,44 @@ +package com.github.chistousov.authorization_backend.models; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class GetLoginResponseModel { + + private String challenge; + + private Client client; + + @JsonProperty("oidc_context") + private Object oidcContext; + + @JsonProperty("request_url") + private String requestUrl; + + @JsonProperty("requested_access_token_audience") + private List requestedAccessTokenAudience; + + @JsonProperty("requested_scope") + private List requestedScope; + + @JsonProperty("session_id") + private String sessionId; + + private Boolean skip; + + private String subject; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/GetLogoutResponseModel.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/GetLogoutResponseModel.java new file mode 100644 index 0000000..2516f5a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/GetLogoutResponseModel.java @@ -0,0 +1,36 @@ +package com.github.chistousov.authorization_backend.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class GetLogoutResponseModel { + private String challenge; + + private Client client; + + @JsonProperty("oidc_context") + private Object oidcContext; + + @JsonProperty("request_url") + private String requestUrl; + + @JsonProperty("rp_initiated") + private Boolean rpInitiated; + + @JsonProperty("sid") + private String sid; + + private String subject; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/PostLoginModel.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/PostLoginModel.java new file mode 100644 index 0000000..7828fa6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/PostLoginModel.java @@ -0,0 +1,27 @@ +package com.github.chistousov.authorization_backend.models; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PostLoginModel { + + @NotBlank(message = "login is blank") + @Size(min = 4, message = "login must be greater than 4") + private String login; + + @NotBlank(message = "password is blank") + @Pattern(regexp = "^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$", message = "password is invalid") + private String password; + + private Boolean isRemember; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/PostRegistrationModel.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/PostRegistrationModel.java new file mode 100644 index 0000000..f95ff24 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/PostRegistrationModel.java @@ -0,0 +1,31 @@ +package com.github.chistousov.authorization_backend.models; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +@EqualsAndHashCode +public class PostRegistrationModel { + + @NotBlank(message = "login is blank") + @Size(min = 4, message = "login must be greater than 4") + private String login; + + @NotBlank(message = "password is blank") + @Pattern(regexp = "^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$", message = "password is invalid") + private String password; + + @NotBlank(message = "orgName is blank") + @Size(min = 4, message = "org name must be greater than 4") + private String orgName; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/PutLogoutModel.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/PutLogoutModel.java new file mode 100644 index 0000000..3d977a5 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/PutLogoutModel.java @@ -0,0 +1,14 @@ +package com.github.chistousov.authorization_backend.models; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PutLogoutModel { + private Boolean isConfirmed; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/ResponseWithRedirectModel.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/ResponseWithRedirectModel.java new file mode 100644 index 0000000..cadab81 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/ResponseWithRedirectModel.java @@ -0,0 +1,24 @@ +package com.github.chistousov.authorization_backend.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +@ToString +public class ResponseWithRedirectModel { + + @JsonProperty("redirect_to") + private String redirectTo; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/SessionForTokenModel.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/SessionForTokenModel.java new file mode 100644 index 0000000..1e1310c --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/SessionForTokenModel.java @@ -0,0 +1,23 @@ +package com.github.chistousov.authorization_backend.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Getter; + +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +public class SessionForTokenModel { + + @JsonProperty("access_token") + private Object accessTokenExtension; + + @JsonProperty("id_token") + private Object idTokenExtension; + + public SessionForTokenModel(SessionForTokenModelBuilder builder) { + this.accessTokenExtension = builder.getAccessTokenExtension(); + this.idTokenExtension = builder.getIdTokenExtension(); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/SessionForTokenModelBuilder.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/SessionForTokenModelBuilder.java new file mode 100644 index 0000000..6a2db84 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/models/SessionForTokenModelBuilder.java @@ -0,0 +1,33 @@ +package com.github.chistousov.authorization_backend.models; + +import lombok.Getter; + +@Getter +public class SessionForTokenModelBuilder { + + private Object accessTokenExtension; + + private Object idTokenExtension; + + public static SessionForTokenModelBuilder builder() { + return new SessionForTokenModelBuilder(); + } + + private SessionForTokenModelBuilder() { + } + + public SessionForTokenModelBuilder setAccessTokenExtension(Object accessTokenExtension) { + this.accessTokenExtension = accessTokenExtension; + return this; + } + + public SessionForTokenModelBuilder setIdTokenExtension(Object idTokenExtension) { + this.idTokenExtension = idTokenExtension; + return this; + } + + public SessionForTokenModel build() { + return new SessionForTokenModel(this); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/services/OryHydraService.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/services/OryHydraService.java new file mode 100644 index 0000000..efa944b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/services/OryHydraService.java @@ -0,0 +1,308 @@ +package com.github.chistousov.authorization_backend.services; + +import java.util.Objects; + +import org.springframework.core.env.Environment; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClient; + +import com.github.chistousov.authorization_backend.models.AcceptConsentRequestModel; +import com.github.chistousov.authorization_backend.models.AcceptLoginRequestModel; +import com.github.chistousov.authorization_backend.models.ErrorModel; +import com.github.chistousov.authorization_backend.models.GetConsentResponseModel; +import com.github.chistousov.authorization_backend.models.GetLoginResponseModel; +import com.github.chistousov.authorization_backend.models.GetLogoutResponseModel; +import com.github.chistousov.authorization_backend.models.ResponseWithRedirectModel; + +import reactor.core.publisher.Mono; + +/** + *

+ * Service for working with admin Ory Hydra + *

+ * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ +@Service +public class OryHydraService { + private static final String LOGIN_CHALLENGE = "login_challenge"; + private static final String CONSENT_CHALLENGE = "consent_challenge"; + private static final String LOGOUT_CHALLENGE = "logout_challenge"; + + private WebClient oryHydraAdminEndPoint; + + public OryHydraService(Environment env) { + + this.oryHydraAdminEndPoint = WebClient + .builder() + .baseUrl(Objects.requireNonNull(env.getProperty("application.ory-hydra.admin.baseURI"))) + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + + // Since Ory Hydra is configured for TLS termination, the header below is + // required + // We pretend that WebClient is a proxy with TLS termination + + // Так как Ory Hydra настроена на TLS termination, то необходим заголовок ниже + // Делаем вид что, WebClient прокси с TLS termination + + .defaultHeader("X-Forwarded-Proto", "https") + .build(); + } + + /** + *

+ * check whether the user was authenticated before and remembered himself in the + * system? + * plus we also get information about the client + *

+ * + *

+ * проверяем аутентифицировался ли раньше пользователь и запомнил себя в + * системе? + * плюс так же получаем информацию о клиенте + *

+ * + * @param loginChallenge - login processing unique ID + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public Mono loginRequestInfo(String loginChallenge) { + return this.oryHydraAdminEndPoint + .get() + .uri( + uriBuilder -> uriBuilder + .path("oauth2/auth/requests/login") + .queryParam(LOGIN_CHALLENGE, loginChallenge) + .build()) + .retrieve() + .bodyToMono(GetLoginResponseModel.class); + } + + /** + *

+ * accept authentication of a specific user + *

+ * + *

+ * принимаем аутентификацию определенного пользователя + *

+ * + * @param loginChallenge - login processing unique ID + * @param acceptLoginRequestModel - authentication data + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public Mono acceptLoginRequest(String loginChallenge, + Mono acceptLoginRequestModel) { + return this.oryHydraAdminEndPoint + .put() + .uri( + uriBuilder -> uriBuilder + .path("oauth2/auth/requests/login/accept") + .queryParam(LOGIN_CHALLENGE, loginChallenge) + .build()) + .body(acceptLoginRequestModel, AcceptLoginRequestModel.class) + .retrieve() + .bodyToMono(ResponseWithRedirectModel.class); + } + + /** + *

+ * deny authentication of a specific user + *

+ * + *

+ * отклоняем аутентификацию определенного пользователя + *

+ * + * @param loginChallenge - login processing unique ID + * @param rejectLoginRequestModel - error data + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public Mono rejectLoginRequest(String loginChallenge, + Mono rejectLoginRequestModel) { + return this.oryHydraAdminEndPoint + .put() + .uri( + uriBuilder -> uriBuilder + .path("oauth2/auth/requests/login/reject") + .queryParam(LOGIN_CHALLENGE, loginChallenge) + .build()) + .body(rejectLoginRequestModel, ErrorModel.class) + .retrieve() + .bodyToMono(ResponseWithRedirectModel.class); + } + + /** + *

+ * check whether the scope check has started in the system? + * plus we also get information about the client + *

+ * + *

+ * проверяем начали ли проверку scope + * в системе? + * плюс так же получаем информацию о клиенте + *

+ * + * @param consentChallenge - scope processing unique identifier + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public Mono consentRequestInfo(String consentChallenge) { + return this.oryHydraAdminEndPoint + .get() + .uri( + uriBuilder -> uriBuilder + .path("oauth2/auth/requests/consent") + .queryParam(CONSENT_CHALLENGE, consentChallenge) + .build()) + .retrieve() + .bodyToMono(GetConsentResponseModel.class); + } + + /** + *

+ * accept scopes of a specific user + *

+ * + *

+ * принимаем scopes определенного пользователя + *

+ * + * @param consentChallenge - scope processing unique identifier + * @param acceptConsentRequestModel - scope data + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public Mono acceptConsentRequest(String consentChallenge, + Mono acceptConsentRequestModel) { + return this.oryHydraAdminEndPoint + .put() + .uri( + uriBuilder -> uriBuilder + .path("oauth2/auth/requests/consent/accept") + .queryParam(CONSENT_CHALLENGE, consentChallenge) + .build()) + .body(acceptConsentRequestModel, AcceptConsentRequestModel.class) + .retrieve() + .bodyToMono(ResponseWithRedirectModel.class); + } + + /** + *

+ * deny authentication of a specific user + *

+ * + *

+ * отклоняем аутентификацию определенного пользователя + *

+ * + * @param consentChallenge - login processing unique ID + * @param rejectConsentRequestModel - error data + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public Mono rejectConsentRequest(String consentChallenge, + Mono rejectConsentRequestModel) { + return this.oryHydraAdminEndPoint + .put() + .uri( + uriBuilder -> uriBuilder + .path("oauth2/auth/requests/consent/reject") + .queryParam(CONSENT_CHALLENGE, consentChallenge) + .build()) + .body(rejectConsentRequestModel, ErrorModel.class) + .retrieve() + .bodyToMono(ResponseWithRedirectModel.class); + } + + /** + *

+ * Getting information by logout + *

+ * + *

+ * Получаем информацию по logout + *

+ * + * @param logoutChallenge - exit handling unique identifier + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public Mono logoutRequestInfo(String logoutChallenge) { + return this.oryHydraAdminEndPoint + .get() + .uri( + uriBuilder -> uriBuilder + .path("oauth2/auth/requests/logout") + .queryParam(LOGOUT_CHALLENGE, logoutChallenge) + .build()) + .retrieve() + .bodyToMono(GetLogoutResponseModel.class); + } + + /** + *

+ * confirm logout + *

+ * + *

+ * подтверждаем logout + *

+ * + * @param logoutChallenge - logout processing unique identifier + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public Mono acceptLogoutRequest(String logoutChallenge) { + return this.oryHydraAdminEndPoint + .put() + .uri( + uriBuilder -> uriBuilder + .path("oauth2/auth/requests/logout/accept") + .queryParam(LOGOUT_CHALLENGE, logoutChallenge) + .build()) + .retrieve() + .bodyToMono(ResponseWithRedirectModel.class); + } + + /** + *

+ * deny logout request + *

+ * + *

+ * отклонить запрос на logout + *

+ * + * @param logoutChallenge - exit handling unique identifier + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public Mono rejectLogoutRequest(String logoutChallenge) { + return this.oryHydraAdminEndPoint + .put() + .uri( + uriBuilder -> uriBuilder + .path("oauth2/auth/requests/logout/reject") + .queryParam(LOGOUT_CHALLENGE, logoutChallenge) + .build()) + .retrieve() + .bodyToMono(ResponseWithRedirectModel.class); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/services/UserService.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/services/UserService.java new file mode 100644 index 0000000..d082670 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/java/com/github/chistousov/authorization_backend/services/UserService.java @@ -0,0 +1,184 @@ +package com.github.chistousov.authorization_backend.services; + +import java.util.List; +import java.util.function.Supplier; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.support.TransactionTemplate; + +import com.github.chistousov.authorization_backend.dao.entities.User; +import com.github.chistousov.authorization_backend.dao.repositories.UserRepository; +import com.github.chistousov.authorization_backend.exceptions.IncorrectPasswordException; +import com.github.chistousov.authorization_backend.exceptions.LoginDoesNotExistException; +import com.github.chistousov.authorization_backend.jacoco_ignore.ExcludeFromJacocoGeneratedReport; +import com.github.chistousov.authorization_backend.models.PostRegistrationModel; + +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Scheduler; + +/** + *

+ * Service for working with the user (creation and verification) + *

+ * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ +@Service +public class UserService { + + private TransactionTemplate transactionTemplate; + private Scheduler jdbcScheduler; + + private UserRepository userRepository; + private PasswordEncoder passwordEncoder; + + @PersistenceContext + private EntityManager em; + + public UserService(UserRepository userRepository, PasswordEncoder passwordEncoderRegister) { + this.userRepository = userRepository; + this.passwordEncoder = passwordEncoderRegister; + } + + @Autowired + public void setTransactionTemplate(TransactionTemplate transactionTemplate) { + this.transactionTemplate = transactionTemplate; + } + + @Autowired + @Qualifier("jdbcScheduler") + public void setJdbcScheduler(Scheduler jdbcScheduler) { + this.jdbcScheduler = jdbcScheduler; + } + + /** + * + *

+ * Create a user + *

+ * + * + * @param postRegistrationModel - registration data + * + * @return user id + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public Mono createUser(PostRegistrationModel postRegistrationModel) { + + return Mono.fromCallable( + () -> { + + String hashPassword = passwordEncoder.encode(postRegistrationModel.getPassword()); + + return transaction(() -> userRepository.createUser( + postRegistrationModel.getLogin(), + hashPassword, + postRegistrationModel.getOrgName())); + }) + .subscribeOn(jdbcScheduler); + + } + + /** + * + *

+ * Get user by login and check user + *

+ * + * + * @param login - login + * + * @param password - password + * + * @return user data + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public Mono getUserAndCheck(String login, String password) { + + return Mono.fromCallable(() -> { + + User user = getUserInDB(login); + + if (passwordEncoder.matches(password, user.getPassword())) { + user.setPassword(""); + + return user; + } else { + throw new IncorrectPasswordException("Password is incorrect! "); + } + }) + .subscribeOn(jdbcScheduler); + } + + /** + * + *

+ * Get user by login + *

+ * + * + * @param login - login + * + * @return user data + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public Mono getUser(String login) { + + return Mono.fromCallable(() -> { + + User user = getUserInDB(login); + user.setPassword(""); + return user; + + }) + .subscribeOn(jdbcScheduler); + } + + private User getUserInDB(String login) { + return transaction(() -> { + + var storedProcedure = em.createNamedStoredProcedureQuery("User.getUserByLogin"); + storedProcedure.setParameter(2, login); + storedProcedure.execute(); + + List users = (List) storedProcedure.getResultList(); + + if (!users.isEmpty()) { + return users.get(0); + } else { + throw new LoginDoesNotExistException("login does not exist"); + } + + }); + } + + /** + *

+ * Additional wrapper for manual transaction to be ignored in JaCoCo + *

+ * + * @param manId - user in the system + * + * @return user information + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + @ExcludeFromJacocoGeneratedReport + private T transaction(Supplier s) { + return transactionTemplate.execute(status -> s.get()); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000..bbfbe77 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,5 @@ +{"properties": [{ + "name": "application.title", + "type": "java.lang.String", + "description": "A description for 'application.title'" +}]} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/resources/application-prod.yml b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/resources/application-prod.yml new file mode 100644 index 0000000..840942e --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/resources/application-prod.yml @@ -0,0 +1,10 @@ +spring: + + datasource: + hikari: + maximum-pool-size: 6 + + +logging: + level: + root: info \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/resources/application.yml b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/resources/application.yml new file mode 100644 index 0000000..0b0e578 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/resources/application.yml @@ -0,0 +1,37 @@ +application: + title: Registration Example Ory Hydra + +spring: + + output: + ansi: + enabled: always + + datasource: + hikari: + maximum-pool-size: 5 + + jpa: + hibernate: + ddl-auto: none + properties: + hibernate: + show_sql: true + use_sql_comments: true + format_sql: true + proc: + param_null_passing: true + +logging: + level: + root: TRACE + +server: + port: 8090 + reactive: + session: + cookie: + name: SESSION-LOGIN-WRAPPER + forward-headers-strategy: framework + error: + include-stacktrace: never \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/resources/banner.txt b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/resources/banner.txt new file mode 100644 index 0000000..a4e1230 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/main/resources/banner.txt @@ -0,0 +1,7 @@ +${AnsiColor.BRIGHT_CYAN} +,---.| o | +| |---..,---.|--- ,---.. .,---.,---.. , +| | ||`---.| | || |`---.| | \ / +`---'` '``---'`---'`---'`---'`---'`---' `' +${AnsiBackground.WHITE}${AnsiColor.BRIGHT_BLACK}${application.title} +Powered by Spring Boot ${spring-boot.version}${AnsiColor.DEFAULT}${AnsiBackground.DEFAULT} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/SupportModule.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/SupportModule.java new file mode 100644 index 0000000..be54921 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/SupportModule.java @@ -0,0 +1,94 @@ +package com.github.chistousov.authorization_backend; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Base64; +import java.util.Random; + +public class SupportModule { + + /** + *

+ * Get query parameters from String + *

+ * + * @param it - String (query parameters) + * + * @return SimpleImmutableEntry - query parameters + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public static SimpleImmutableEntry splitQueryParameter(String it) { + final int idx = it.indexOf("="); + final String key = idx > 0 ? it.substring(0, idx) : it; + final String value = idx > 0 && it.length() > idx + 1 ? it.substring(idx + 1) : null; + return new SimpleImmutableEntry<>( + URLDecoder.decode(key, StandardCharsets.UTF_8), + URLDecoder.decode(value, StandardCharsets.UTF_8)); + } + + /** + *

+ * Generate random String + *

+ * + * @return random String + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public static String generateRandomStr() { + int leftLimit = 97; + int rightLimit = 122; + int targetStringLength = 30; + Random random = new Random(); + StringBuilder buffer = new StringBuilder(targetStringLength); + for (int i = 0; i < targetStringLength; i++) { + int randomLimitedInt = leftLimit + (int) (random.nextFloat() * (rightLimit - leftLimit + 1)); + buffer.append((char) randomLimitedInt); + } + return buffer.toString(); + } + + /** + *

+ * Generate random Code Verifier (PKCE) + *

+ * + * @return code verifier + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public static String generateCodeVerifier() throws UnsupportedEncodingException { + SecureRandom secureRandom = new SecureRandom(); + byte[] codeVerifier = new byte[32]; + secureRandom.nextBytes(codeVerifier); + return Base64.getUrlEncoder().withoutPadding().encodeToString(codeVerifier); + } + + /** + *

+ * Generate random Code Challange (PKCE) + *

+ * + * @return code challange + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + public static String generateCodeChallange(String codeVerifier) + throws UnsupportedEncodingException, NoSuchAlgorithmException { + byte[] bytes = codeVerifier.getBytes("US-ASCII"); + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + messageDigest.update(bytes, 0, bytes.length); + byte[] digest = messageDigest.digest(); + return Base64.getUrlEncoder().withoutPadding().encodeToString(digest); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/TestAccessTokenExtension.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/TestAccessTokenExtension.java new file mode 100644 index 0000000..e48a1b2 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/TestAccessTokenExtension.java @@ -0,0 +1,23 @@ +package com.github.chistousov.authorization_backend; + +public class TestAccessTokenExtension { + private Long id; + private String name; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setId(Long id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/TestIdTokenExtension.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/TestIdTokenExtension.java new file mode 100644 index 0000000..ab07bce --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/TestIdTokenExtension.java @@ -0,0 +1,14 @@ +package com.github.chistousov.authorization_backend; + +public class TestIdTokenExtension { + private String hash; + + public String getHash() { + return hash; + } + + public void setHash(String hash) { + this.hash = hash; + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/controllers/ConsentProviderControllerTest.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/controllers/ConsentProviderControllerTest.java new file mode 100644 index 0000000..21fbf4d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/controllers/ConsentProviderControllerTest.java @@ -0,0 +1,636 @@ +package com.github.chistousov.authorization_backend.controllers; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.times; + +import java.io.UnsupportedEncodingException; +import java.time.Duration; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.PropertySource; +import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.web.reactive.server.WebTestClient; + +import com.github.chistousov.authorization_backend.SpringSecurityConfiguration; +import com.github.chistousov.authorization_backend.models.Client; +import com.github.chistousov.authorization_backend.models.GetConsentResponseModel; +import com.github.chistousov.authorization_backend.models.ResponseWithRedirectModel; +import com.github.chistousov.authorization_backend.services.OryHydraService; + +import reactor.core.publisher.Mono; + +@WebFluxTest(controllers = { ConsentProviderController.class }) +@PropertySource("classpath:application.yml") +@Import(SpringSecurityConfiguration.class) +class ConsentProviderControllerTest { + + /*------- const (begin) ------- */ + + private static final String frontendURI = "http://some-frontend"; + + private static final String CONSENT_CHALLENGE = "consent_challenge"; + + private static final String SESSION = "SESSION"; + + /*------- const (end) ------- */ + + @MockBean + private OryHydraService oryHydraService; + + @Autowired + private WebTestClient thisServerWebTestClient; + + // session this app + private String sessionValueThisApp; + + @DynamicPropertySource + public static void settings(DynamicPropertyRegistry registry) + throws UnsupportedEncodingException { + + registry.add("application.ory-hydra.frontend.consent-redirectURI", () -> frontendURI); + + } + + @BeforeEach + void setUp() { + thisServerWebTestClient = thisServerWebTestClient.mutate() + .responseTimeout(Duration.ofMinutes(5)) + .build(); + sessionValueThisApp = null; + } + + @Test + @DisplayName("full consent flow") + void fullConsentFlow() { + + // given (instead of when) + + final String consentChallenge = "some_consent_challenge"; + + final String clientId = "some_client_id"; + + final List scopes = List.of("read", "write"); + + final String redirectTo = "http://some-red"; + + final String subject = "some subject"; + + final GetConsentResponseModel getConsentResponseModel = GetConsentResponseModel + .builder() + .subject(subject) + .skip(false) + .requestedScope(scopes) + .client( + Client + .builder() + .skipConsent(false) + .clientId(clientId) + .build()) + .build(); + + final ResponseWithRedirectModel responseWithRedirectModel = ResponseWithRedirectModel.builder() + .redirectTo(redirectTo) + .build(); + + given(oryHydraService.consentRequestInfo(consentChallenge)).willReturn(Mono.just(getConsentResponseModel)); + + given(oryHydraService.acceptConsentRequest(eq(consentChallenge), any())) + .willReturn(Mono.just(responseWithRedirectModel)); + + // when + + // get consent + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/consent") + .queryParam(CONSENT_CHALLENGE, consentChallenge) + .build()) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", frontendURI) + .expectCookie() + .exists(SESSION) + .expectCookie() + .value(SESSION, str -> sessionValueThisApp = str); + + // get client name + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/consent/client-name") + .build()) + .cookie(SESSION, sessionValueThisApp) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo(clientId); + + // get subject + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/consent/subject") + .build()) + .cookie(SESSION, sessionValueThisApp) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo(subject); + + // get scope + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/consent/scopes") + .build()) + .cookie(SESSION, sessionValueThisApp) + .exchange() + .expectStatus() + .isOk() + .expectBody() + .jsonPath("$").isNotEmpty() + .jsonPath("$").isArray() + .jsonPath("$.length()").isEqualTo(2) + .jsonPath("$[0]").isEqualTo(scopes.get(0)) + .jsonPath("$[1]").isEqualTo(scopes.get(1)); + + // post consent + thisServerWebTestClient + .mutateWith(SecurityMockServerConfigurers.csrf()) + .put() + .uri( + uriBuilder -> uriBuilder + .path("/consent") + .queryParam("is-remember", true) + .build()) + .cookie(SESSION, sessionValueThisApp) + .exchange() + .expectStatus() + .isCreated() + .expectBody() + .jsonPath("$").isNotEmpty() + .jsonPath("$").isMap() + .jsonPath("$.length()").isEqualTo(1) + .jsonPath("$.redirect_to").isNotEmpty() + .jsonPath("$.redirect_to").isEqualTo(responseWithRedirectModel.getRedirectTo()); + + // then (instead of verify) + then(oryHydraService) + .should(times(5)) + .consentRequestInfo(consentChallenge); + + then(oryHydraService) + .should() + .acceptConsentRequest(eq(consentChallenge), any()); + + } + + @Test + @DisplayName("reconsent1") + void reauthentication() { + + // given (instead of when) + + final String consentChallenge = "some_consent_challenge"; + + final List scopes = List.of("read", "write"); + + final String redirectTo = "http://some-red"; + + final GetConsentResponseModel getConsentResponseModel = GetConsentResponseModel + .builder() + .skip(true) + .requestedScope(scopes) + .build(); + + final ResponseWithRedirectModel responseWithRedirectModel = ResponseWithRedirectModel.builder() + .redirectTo(redirectTo) + .build(); + + given(oryHydraService.consentRequestInfo(consentChallenge)).willReturn(Mono.just(getConsentResponseModel)); + + given(oryHydraService.acceptConsentRequest(eq(consentChallenge), any())) + .willReturn(Mono.just(responseWithRedirectModel)); + + // when + + // get consent + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/consent") + .queryParam(CONSENT_CHALLENGE, consentChallenge) + .build()) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", responseWithRedirectModel.getRedirectTo()); + + // then (instead of verify) + then(oryHydraService) + .should() + .consentRequestInfo(consentChallenge); + + then(oryHydraService) + .should() + .acceptConsentRequest(eq(consentChallenge), any()); + + } + + @Test + @DisplayName("reconsent2") + void reauthentication2() { + + // given (instead of when) + + final String consentChallenge = "some_consent_challenge"; + + final List scopes = List.of("read", "write"); + + final String redirectTo = "http://some-red"; + + final GetConsentResponseModel getConsentResponseModel = GetConsentResponseModel + .builder() + .skip(false) + .requestedScope(scopes) + .client( + Client + .builder() + .skipConsent(true) + .build()) + .build(); + + final ResponseWithRedirectModel responseWithRedirectModel = ResponseWithRedirectModel.builder() + .redirectTo(redirectTo) + .build(); + + given(oryHydraService.consentRequestInfo(consentChallenge)).willReturn(Mono.just(getConsentResponseModel)); + + given(oryHydraService.acceptConsentRequest(eq(consentChallenge), any())) + .willReturn(Mono.just(responseWithRedirectModel)); + + // when + + // get consent + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/consent") + .queryParam(CONSENT_CHALLENGE, consentChallenge) + .build()) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", responseWithRedirectModel.getRedirectTo()); + + // then (instead of verify) + then(oryHydraService) + .should() + .consentRequestInfo(consentChallenge); + + then(oryHydraService) + .should() + .acceptConsentRequest(eq(consentChallenge), any()); + + } + + @Test + @DisplayName("client name 1 (clientName isBlack)") + void clientName1() { + + // given (instead of when) + + final String consentChallenge = "some_consent_challenge"; + + final String clientId = "some_client_id"; + + final List scopes = List.of("read", "write"); + + final String redirectTo = "http://some-red"; + + final GetConsentResponseModel getConsentResponseModel = GetConsentResponseModel + .builder() + .skip(false) + .requestedScope(scopes) + .client( + Client + .builder() + .skipConsent(false) + .clientName("\t") + .clientId(clientId) + .build()) + .build(); + + final ResponseWithRedirectModel responseWithRedirectModel = ResponseWithRedirectModel.builder() + .redirectTo(redirectTo) + .build(); + + given(oryHydraService.consentRequestInfo(consentChallenge)).willReturn(Mono.just(getConsentResponseModel)); + + given(oryHydraService.acceptConsentRequest(eq(consentChallenge), any())) + .willReturn(Mono.just(responseWithRedirectModel)); + + // when + + // get consent + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/consent") + .queryParam(CONSENT_CHALLENGE, consentChallenge) + .build()) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", frontendURI) + .expectCookie() + .exists(SESSION) + .expectCookie() + .value(SESSION, str -> sessionValueThisApp = str); + + // get client name + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/consent/client-name") + .build()) + .cookie(SESSION, sessionValueThisApp) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo(clientId); + + } + + @Test + @DisplayName("client name 2 (clientName == null)") + void clientName2() { + + // given (instead of when) + + final String consentChallenge = "some_consent_challenge"; + + final String clientId = "some_client_id"; + + final List scopes = List.of("read", "write"); + + final String redirectTo = "http://some-red"; + + final GetConsentResponseModel getConsentResponseModel = GetConsentResponseModel + .builder() + .skip(false) + .requestedScope(scopes) + .client( + Client + .builder() + .skipConsent(false) + .clientName(null) + .clientId(clientId) + .build()) + .build(); + + final ResponseWithRedirectModel responseWithRedirectModel = ResponseWithRedirectModel.builder() + .redirectTo(redirectTo) + .build(); + + given(oryHydraService.consentRequestInfo(consentChallenge)).willReturn(Mono.just(getConsentResponseModel)); + + given(oryHydraService.acceptConsentRequest(eq(consentChallenge), any())) + .willReturn(Mono.just(responseWithRedirectModel)); + + // when + + // get consent + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/consent") + .queryParam(CONSENT_CHALLENGE, consentChallenge) + .build()) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", frontendURI) + .expectCookie() + .exists(SESSION) + .expectCookie() + .value(SESSION, str -> sessionValueThisApp = str); + + // get client name + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/consent/client-name") + .build()) + .cookie(SESSION, sessionValueThisApp) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo(clientId); + + } + + @Test + @DisplayName("client name 3 (clientName != null)") + void clientName3() { + + // given (instead of when) + + final String consentChallenge = "some_consent_challenge"; + + final String clientName = "some_client_name"; + + final List scopes = List.of("read", "write"); + + final String redirectTo = "http://some-red"; + + final GetConsentResponseModel getConsentResponseModel = GetConsentResponseModel + .builder() + .skip(false) + .requestedScope(scopes) + .client( + Client + .builder() + .skipConsent(false) + .clientName(clientName) + .build()) + .build(); + + final ResponseWithRedirectModel responseWithRedirectModel = ResponseWithRedirectModel.builder() + .redirectTo(redirectTo) + .build(); + + given(oryHydraService.consentRequestInfo(consentChallenge)).willReturn(Mono.just(getConsentResponseModel)); + + given(oryHydraService.acceptConsentRequest(eq(consentChallenge), any())) + .willReturn(Mono.just(responseWithRedirectModel)); + + // when + + // get consent + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/consent") + .queryParam(CONSENT_CHALLENGE, consentChallenge) + .build()) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", frontendURI) + .expectCookie() + .exists(SESSION) + .expectCookie() + .value(SESSION, str -> sessionValueThisApp = str); + + // get client name + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/consent/client-name") + .build()) + .cookie(SESSION, sessionValueThisApp) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo(clientName); + + } + + @Test + @DisplayName("full consent flow (cancel)") + void cancelFlow() { + + // given (instead of when) + + final String consentChallenge = "some_consent_challenge"; + + final String clientId = "some_client_id"; + + final List scopes = List.of("read", "write"); + + final String redirectTo = "http://some-red"; + + final GetConsentResponseModel getConsentResponseModel = GetConsentResponseModel + .builder() + .skip(false) + .requestedScope(scopes) + .client( + Client + .builder() + .skipConsent(false) + .clientId(clientId) + .build()) + .build(); + + final ResponseWithRedirectModel responseWithRedirectModel = ResponseWithRedirectModel.builder() + .redirectTo(redirectTo) + .build(); + + given(oryHydraService.consentRequestInfo(consentChallenge)).willReturn(Mono.just(getConsentResponseModel)); + + given(oryHydraService.rejectConsentRequest(eq(consentChallenge), any())) + .willReturn(Mono.just(responseWithRedirectModel)); + + // when + + // get consent + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/consent") + .queryParam(CONSENT_CHALLENGE, consentChallenge) + .build()) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", frontendURI) + .expectCookie() + .exists(SESSION) + .expectCookie() + .value(SESSION, str -> sessionValueThisApp = str); + + // post consent + thisServerWebTestClient + .mutateWith(SecurityMockServerConfigurers.csrf()) + .delete() + .uri( + uriBuilder -> uriBuilder + .path("/consent/cancel") + .build()) + .cookie(SESSION, sessionValueThisApp) + .exchange() + .expectStatus() + .isCreated() + .expectBody() + .jsonPath("$").isNotEmpty() + .jsonPath("$").isMap() + .jsonPath("$.length()").isEqualTo(1) + .jsonPath("$.redirect_to").isNotEmpty() + .jsonPath("$.redirect_to").isEqualTo(responseWithRedirectModel.getRedirectTo()); + + // then (instead of verify) + then(oryHydraService) + .should() + .consentRequestInfo(consentChallenge); + + then(oryHydraService) + .should() + .rejectConsentRequest(eq(consentChallenge), any()); + + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/controllers/LoginProviderControllerTest.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/controllers/LoginProviderControllerTest.java new file mode 100644 index 0000000..ad0800e --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/controllers/LoginProviderControllerTest.java @@ -0,0 +1,389 @@ +package com.github.chistousov.authorization_backend.controllers; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; + +import java.io.UnsupportedEncodingException; +import java.time.Duration; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.PropertySource; +import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.web.reactive.server.WebTestClient; +import com.github.chistousov.authorization_backend.SpringSecurityConfiguration; +import com.github.chistousov.authorization_backend.dao.entities.User; +import com.github.chistousov.authorization_backend.exceptions.IncorrectPasswordException; +import com.github.chistousov.authorization_backend.models.GetLoginResponseModel; +import com.github.chistousov.authorization_backend.models.PostLoginModel; +import com.github.chistousov.authorization_backend.models.ResponseWithRedirectModel; +import com.github.chistousov.authorization_backend.services.OryHydraService; +import com.github.chistousov.authorization_backend.services.UserService; + +import reactor.core.publisher.Mono; + +@WebFluxTest(controllers = { LoginProviderController.class }) +@PropertySource("classpath:application.yml") +@Import(SpringSecurityConfiguration.class) +public class LoginProviderControllerTest { + + /*------- const (begin) ------- */ + + private static final String frontendURI = "http://some-frontend"; + + private static final int numberOfLoginAttempts = 3; + + private static final String LOGIN_CHALLENGE = "login_challenge"; + + private static final String SESSION = "SESSION"; + + /*------- const (end) ------- */ + + @MockBean + private UserService userService; + @MockBean + private OryHydraService oryHydraService; + + @Autowired + private WebTestClient thisServerWebTestClient; + + // session this app + private String sessionValueThisApp; + + @DynamicPropertySource + public static void settings(DynamicPropertyRegistry registry) + throws UnsupportedEncodingException { + + registry.add("application.ory-hydra.frontend.login-redirectURI", () -> frontendURI); + + registry.add("application.ory-hydra.number-of-login-attempts", () -> numberOfLoginAttempts); + + } + + @BeforeEach + void setUp() { + thisServerWebTestClient = thisServerWebTestClient.mutate() + .responseTimeout(Duration.ofMinutes(5)) + .build(); + sessionValueThisApp = null; + } + + @Test + @DisplayName("full login flow") + void fullLoginFlow() { + + // given (instead of when) + + final String loginChallenge = "some_login_challenge"; + + final String login = "some_login"; + final String password = "some_super_pass"; + final boolean isRemember = true; + + final String redirectTo = "http://some-red"; + + final GetLoginResponseModel getLoginResponseModel = GetLoginResponseModel + .builder() + .skip(false) + .build(); + + final PostLoginModel postLoginModel = PostLoginModel.builder() + .login(login) + .password(password) + .isRemember(isRemember) + .build(); + + final User user = User.builder() + .login(login) + .build(); + + final ResponseWithRedirectModel responseWithRedirectModel = ResponseWithRedirectModel.builder() + .redirectTo(redirectTo) + .build(); + + given(oryHydraService.loginRequestInfo(loginChallenge)).willReturn(Mono.just(getLoginResponseModel)); + + given(userService.getUserAndCheck(login, password)).willReturn(Mono.just(user)); + + given(oryHydraService.acceptLoginRequest(eq(loginChallenge), any())) + .willReturn(Mono.just(responseWithRedirectModel)); + + // when + + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/login") + .queryParam(LOGIN_CHALLENGE, loginChallenge) + .build()) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", frontendURI) + .expectCookie() + .exists(SESSION) + .expectCookie() + .value(SESSION, str -> sessionValueThisApp = str); + + thisServerWebTestClient + .mutateWith(SecurityMockServerConfigurers.csrf()) + .post() + .uri( + uriBuilder -> uriBuilder + .path("/login") + .build()) + .body(Mono.just(postLoginModel), PostLoginModel.class) + .cookie(SESSION, sessionValueThisApp) + .exchange() + .expectStatus() + .isCreated() + .expectBody() + .jsonPath("$").isNotEmpty() + .jsonPath("$").isMap() + .jsonPath("$.length()").isEqualTo(1) + .jsonPath("$.redirect_to").isNotEmpty() + .jsonPath("$.redirect_to").isEqualTo(responseWithRedirectModel.getRedirectTo()); + + // then (instead of verify) + then(oryHydraService) + .should() + .loginRequestInfo(loginChallenge); + + then(userService) + .should() + .getUserAndCheck(login, password); + + then(oryHydraService) + .should() + .acceptLoginRequest(eq(loginChallenge), any()); + + } + + @Test + @DisplayName("reauthentication") + void reauthentication() { + + // given (instead of when) + + final String subject = "some_subject"; + + final String loginChallenge = "some_login_challenge"; + + final String redirectTo = "http://some-red"; + + final GetLoginResponseModel getLoginResponseModel = GetLoginResponseModel + .builder() + .skip(true) + .subject(subject) + .build(); + + final User user = User.builder() + .login(subject) + .build(); + + final ResponseWithRedirectModel responseWithRedirectModel = ResponseWithRedirectModel.builder() + .redirectTo(redirectTo) + .build(); + + given(oryHydraService.loginRequestInfo(loginChallenge)).willReturn(Mono.just(getLoginResponseModel)); + + given(userService.getUser(subject)).willReturn(Mono.just(user)); + + given(oryHydraService.acceptLoginRequest(eq(loginChallenge), any())) + .willReturn(Mono.just(responseWithRedirectModel)); + + // when + + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/login") + .queryParam(LOGIN_CHALLENGE, loginChallenge) + .build()) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", responseWithRedirectModel.getRedirectTo()); + + // then (instead of verify) + then(oryHydraService) + .should() + .loginRequestInfo(loginChallenge); + + then(userService) + .should() + .getUser(subject); + + then(oryHydraService) + .should() + .acceptLoginRequest(eq(loginChallenge), any()); + + } + + @Test + @DisplayName("Maximum number of login attempts exceeded") + void exceededTheNumberOfLoginAttempts() { + // given (instead of when) + + final String loginChallenge = "some_login_challenge"; + + final String redirectTo = "http://some-red"; + + final String login = "some_login"; + + final String password1 = "qwerty1"; + final String password2 = "qwerty2"; + final String password3 = "qwerty3"; + + PostLoginModel postLoginModel1 = new PostLoginModel(login, password1, true); + PostLoginModel postLoginModel2 = new PostLoginModel(login, password2, true); + PostLoginModel postLoginModel3 = new PostLoginModel(login, password3, true); + + var ex = new IncorrectPasswordException("Password is incorrect! "); + + final GetLoginResponseModel getLoginResponseModel = GetLoginResponseModel + .builder() + .skip(false) + .build(); + + final ResponseWithRedirectModel responseWithRedirectModel = ResponseWithRedirectModel.builder() + .redirectTo(redirectTo) + .build(); + + given(oryHydraService.loginRequestInfo(loginChallenge)).willReturn(Mono.just(getLoginResponseModel)); + + given(userService.getUserAndCheck(login, password1)) + .willThrow(ex); + given(userService.getUserAndCheck(login, password2)) + .willThrow(ex); + given(userService.getUserAndCheck(login, password3)) + .willThrow(ex); + + given(oryHydraService.rejectLoginRequest(eq(loginChallenge), any())) + .willReturn(Mono.just(responseWithRedirectModel)); + + // when + + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/login") + .queryParam(LOGIN_CHALLENGE, loginChallenge) + .build()) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", frontendURI) + .expectCookie() + .exists(SESSION) + .expectCookie() + .value(SESSION, str -> sessionValueThisApp = str); + + // 1 + + thisServerWebTestClient + .mutateWith(SecurityMockServerConfigurers.csrf()) + .post() + .uri( + uriBuilder -> uriBuilder + .path("/login") + .build()) + .body(Mono.just(postLoginModel1), PostLoginModel.class) + .cookie(SESSION, sessionValueThisApp) + .exchange() + .expectStatus() + .isBadRequest(); + + // 2 + + thisServerWebTestClient + .mutateWith(SecurityMockServerConfigurers.csrf()) + .post() + .uri( + uriBuilder -> uriBuilder + .path("/login") + .build()) + .body(Mono.just(postLoginModel2), PostLoginModel.class) + .cookie(SESSION, sessionValueThisApp) + .exchange() + .expectStatus() + .isBadRequest(); + + // 3 + + thisServerWebTestClient + .mutateWith(SecurityMockServerConfigurers.csrf()) + .post() + .uri( + uriBuilder -> uriBuilder + .path("/login") + .build()) + .body(Mono.just(postLoginModel3), PostLoginModel.class) + .cookie(SESSION, sessionValueThisApp) + .exchange() + .expectStatus() + .isBadRequest(); + + thisServerWebTestClient + .mutateWith(SecurityMockServerConfigurers.csrf()) + .post() + .uri( + uriBuilder -> uriBuilder + .path("/login") + .build()) + .body(Mono.just(postLoginModel1), PostLoginModel.class) + .cookie(SESSION, sessionValueThisApp) + .exchange() + .expectStatus() + .isCreated() + .expectBody() + .jsonPath("$").isNotEmpty() + .jsonPath("$").isMap() + .jsonPath("$.length()").isEqualTo(1) + .jsonPath("$.redirect_to").isNotEmpty() + .jsonPath("$.redirect_to").isEqualTo(redirectTo); + + // then (instead of verify) + + then(oryHydraService) + .should() + .loginRequestInfo(loginChallenge); + + then(userService) + .should() + .getUserAndCheck(login, password1); + + then(userService) + .should() + .getUserAndCheck(login, password2); + + then(userService) + .should() + .getUserAndCheck(login, password3); + + then(oryHydraService) + .should() + .rejectLoginRequest(eq(loginChallenge), any()); + + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/controllers/LogoutProviderControllerTest.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/controllers/LogoutProviderControllerTest.java new file mode 100644 index 0000000..5f5530d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/controllers/LogoutProviderControllerTest.java @@ -0,0 +1,227 @@ +package com.github.chistousov.authorization_backend.controllers; + +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; + +import java.io.UnsupportedEncodingException; +import java.time.Duration; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.PropertySource; +import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.web.reactive.server.WebTestClient; + +import com.github.chistousov.authorization_backend.SpringSecurityConfiguration; +import com.github.chistousov.authorization_backend.models.GetLogoutResponseModel; +import com.github.chistousov.authorization_backend.models.PutLogoutModel; +import com.github.chistousov.authorization_backend.models.ResponseWithRedirectModel; +import com.github.chistousov.authorization_backend.services.OryHydraService; + +import reactor.core.publisher.Mono; + +@WebFluxTest(controllers = { LogoutProviderController.class }) +@PropertySource("classpath:application.yml") +@Import(SpringSecurityConfiguration.class) +class LogoutProviderControllerTest { + + /*------- const (begin) ------- */ + + private static final String frontendURI = "http://some-frontend"; + + private static final String frontendURICancel = "http://some-frontend2"; + + private static final String LOGOUT_CHALLENGE = "logout_challenge"; + + private static final String SESSION = "SESSION"; + + /*------- const (end) ------- */ + + @MockBean + private OryHydraService oryHydraService; + + @Autowired + private WebTestClient thisServerWebTestClient; + + // session this app + private String sessionValueThisApp; + + @DynamicPropertySource + public static void settings(DynamicPropertyRegistry registry) + throws UnsupportedEncodingException { + + registry.add("application.ory-hydra.frontend.logout-redirectURI", () -> frontendURI); + registry.add("application.ory-hydra.frontend.logout-cancel-redirectURI", () -> frontendURICancel); + + } + + @BeforeEach + void setUp() { + thisServerWebTestClient = thisServerWebTestClient.mutate() + .responseTimeout(Duration.ofMinutes(5)) + .build(); + sessionValueThisApp = null; + } + + @Test + @DisplayName("full logout flow") + void fullLogoutFlow() { + // given (instead of when) + + final String logoutChallenge = "some_logout_challenge"; + + final String redirectTo = "http://some-red"; + + final GetLogoutResponseModel getLogoutResponseModel = GetLogoutResponseModel + .builder() + .subject("some subject") + .build(); + + final PutLogoutModel putLogoutModel = PutLogoutModel + .builder() + .isConfirmed(true) + .build(); + + final ResponseWithRedirectModel responseWithRedirectModel = ResponseWithRedirectModel.builder() + .redirectTo(redirectTo) + .build(); + + given(oryHydraService.logoutRequestInfo(logoutChallenge)).willReturn(Mono.just(getLogoutResponseModel)); + + given(oryHydraService.acceptLogoutRequest(logoutChallenge)) + .willReturn(Mono.just(responseWithRedirectModel)); + // when + + // get logout + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/logout") + .queryParam(LOGOUT_CHALLENGE, logoutChallenge) + .build()) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", frontendURI) + .expectCookie() + .exists(SESSION) + .expectCookie() + .value(SESSION, str -> sessionValueThisApp = str); + + // put logout + thisServerWebTestClient + .mutateWith(SecurityMockServerConfigurers.csrf()) + .put() + .uri( + uriBuilder -> uriBuilder + .path("/logout") + .build()) + .cookie(SESSION, sessionValueThisApp) + .body(Mono.just(putLogoutModel), PutLogoutModel.class) + .exchange() + .expectStatus() + .isCreated() + .expectBody() + .jsonPath("$").isNotEmpty() + .jsonPath("$").isMap() + .jsonPath("$.length()").isEqualTo(1) + .jsonPath("$.redirect_to").isNotEmpty() + .jsonPath("$.redirect_to").isEqualTo(responseWithRedirectModel.getRedirectTo()); + + // then (instead of verify) + then(oryHydraService) + .should() + .logoutRequestInfo(logoutChallenge); + + then(oryHydraService) + .should() + .acceptLogoutRequest(logoutChallenge); + + } + + @Test + @DisplayName("full logout flow (cancel)") + void fullLogoutCancelFlow() { + // given (instead of when) + + final String logoutChallenge = "some_logout_challenge"; + + final GetLogoutResponseModel getLogoutResponseModel = GetLogoutResponseModel + .builder() + .subject("some subject") + .build(); + + final PutLogoutModel putLogoutModel = PutLogoutModel + .builder() + .isConfirmed(false) + .build(); + + given(oryHydraService.logoutRequestInfo(logoutChallenge)).willReturn(Mono.just(getLogoutResponseModel)); + + given(oryHydraService.rejectLogoutRequest(logoutChallenge)) + .willReturn(Mono.empty()); + // when + + // get logout + thisServerWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/logout") + .queryParam(LOGOUT_CHALLENGE, logoutChallenge) + .build()) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", frontendURI) + .expectCookie() + .exists(SESSION) + .expectCookie() + .value(SESSION, str -> sessionValueThisApp = str); + + // put logout + thisServerWebTestClient + .mutateWith(SecurityMockServerConfigurers.csrf()) + .put() + .uri( + uriBuilder -> uriBuilder + .path("/logout") + .build()) + .cookie(SESSION, sessionValueThisApp) + .body(Mono.just(putLogoutModel), PutLogoutModel.class) + .exchange() + .expectStatus() + .isCreated() + .expectBody() + .jsonPath("$").isNotEmpty() + .jsonPath("$").isMap() + .jsonPath("$.length()").isEqualTo(1) + .jsonPath("$.redirect_to").isNotEmpty() + .jsonPath("$.redirect_to").isEqualTo(frontendURICancel); + + // then (instead of verify) + then(oryHydraService) + .should() + .logoutRequestInfo(logoutChallenge); + + then(oryHydraService) + .should() + .rejectLogoutRequest(logoutChallenge); + + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/controllers/registration/RegistrationControllerTest.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/controllers/registration/RegistrationControllerTest.java new file mode 100644 index 0000000..73ab50a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/controllers/registration/RegistrationControllerTest.java @@ -0,0 +1,115 @@ +package com.github.chistousov.authorization_backend.controllers.registration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; + +import org.assertj.core.util.Arrays; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers; +import org.springframework.test.web.reactive.server.WebTestClient; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.chistousov.authorization_backend.SpringSecurityConfiguration; +import com.github.chistousov.authorization_backend.models.PostRegistrationModel; +import com.github.chistousov.authorization_backend.services.UserService; + +import reactor.core.publisher.Mono; + +@WebFluxTest(controllers = { RegistrationController.class }) +@Import(SpringSecurityConfiguration.class) +public class RegistrationControllerTest { + + @Autowired + private WebTestClient thisServerWebTestClient; + + @MockBean + private UserService userService; + + private Long userIdActual = -1L; + + @Test + @DisplayName("registration is successful") + void testPostRegistrationSuccessful() { + + // given (instead of when) + + final Long userIdExpected = 1L; + + final PostRegistrationModel postRegistrationModel = PostRegistrationModel + .builder() + .login("somelogin") + .password("zxcG2!DadD@1vxc2") + .orgName("someOrg") + .build(); + + given(userService.createUser(postRegistrationModel)).willReturn(Mono.just(userIdExpected)); + + // when + + thisServerWebTestClient + .mutateWith(SecurityMockServerConfigurers.csrf()) + .post() + .uri("/registration") + .body(Mono.just(postRegistrationModel), PostRegistrationModel.class) + .exchange() + .expectStatus() + .isCreated() + .expectBody(Long.class) + .value(id -> userIdActual = id); + + // then (instead of verify) + + assertThat(userIdActual).isEqualTo(userIdExpected); + + then(userService) + .should() + .createUser(postRegistrationModel); + + } + + @Test + @DisplayName("registration is fault") + void testPostRegistrationFault() throws JsonProcessingException { + + // given (instead of when) + + final ObjectMapper mapper = new ObjectMapper(); + final String exMsgsExpected = mapper.writeValueAsString( + Arrays.array("login must be greater than 4", + "password is invalid", + "org name must be greater than 4")); + + final PostRegistrationModel postRegistrationModel = PostRegistrationModel + .builder() + .login("zxc") + .password("12") + .orgName("qwe") + .build(); + + // when + + thisServerWebTestClient + .mutateWith(SecurityMockServerConfigurers.csrf()) + .post() + .uri("/registration") + .body(Mono.just(postRegistrationModel), PostRegistrationModel.class) + .exchange() + .expectStatus() + .isBadRequest() + .expectBody() + .jsonPath("$").isNotEmpty() + .jsonPath("$").isArray() + .jsonPath("$.length()").isEqualTo(3) + .jsonPath(String.format("[?($.* anyof %s)]", exMsgsExpected)).isNotEmpty(); + + // then (instead of verify) + + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/models/AcceptLoginRequestModelTest.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/models/AcceptLoginRequestModelTest.java new file mode 100644 index 0000000..a40ddea --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/models/AcceptLoginRequestModelTest.java @@ -0,0 +1,45 @@ +package com.github.chistousov.authorization_backend.models; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class AcceptLoginRequestModelTest { + + @Test + @DisplayName("AcceptLoginRequestModel: subject is null") + void testConstructorAcceptLoginRequestModel1() { + // given (instead of when) + + // when + + Throwable thrown = catchThrowable(() -> AcceptLoginRequestModelBuilder + .builder() + .setSubject(null) + .build()); + + // then (instead of verify) + + assertThat(thrown).hasMessageContaining("Subject not set"); + + } + + @Test + @DisplayName("AcceptLoginRequestModel: subject is blank") + void testConstructorAcceptLoginRequestModel2() { + // given (instead of when) + + // when + + Throwable thrown = catchThrowable(() -> AcceptLoginRequestModelBuilder + .builder() + .setSubject("") + .build()); + + // then (instead of verify) + + assertThat(thrown).hasMessageContaining("Subject not set"); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/models/CreateClientModel.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/models/CreateClientModel.java new file mode 100644 index 0000000..2e947c3 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/models/CreateClientModel.java @@ -0,0 +1,61 @@ +package com.github.chistousov.authorization_backend.models; + +import java.util.List; + +public class CreateClientModel { + private String client_id; + private List redirect_uris; + private String client_secret; + private List grant_types; + private List response_types; + private String scope; + + public String getClient_id() { + return client_id; + } + + public void setClient_id(String client_id) { + this.client_id = client_id; + } + + public List getRedirect_uris() { + return redirect_uris; + } + + public void setRedirect_uris(List redirect_uris) { + this.redirect_uris = redirect_uris; + } + + public String getClient_secret() { + return client_secret; + } + + public void setClient_secret(String client_secret) { + this.client_secret = client_secret; + } + + public List getGrant_types() { + return grant_types; + } + + public void setGrant_types(List grant_types) { + this.grant_types = grant_types; + } + + public List getResponse_types() { + return response_types; + } + + public void setResponse_types(List response_types) { + this.response_types = response_types; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/services/OryHydraServiceTest.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/services/OryHydraServiceTest.java new file mode 100644 index 0000000..7816a32 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/services/OryHydraServiceTest.java @@ -0,0 +1,835 @@ +package com.github.chistousov.authorization_backend.services; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.nio.file.Path; +import java.security.NoSuchAlgorithmException; +import java.time.Duration; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.testcontainers.containers.DockerComposeContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import com.github.chistousov.authorization_backend.SupportModule; +import com.github.chistousov.authorization_backend.TestAccessTokenExtension; +import com.github.chistousov.authorization_backend.TestIdTokenExtension; +import com.github.chistousov.authorization_backend.models.AcceptConsentRequestModelBuilder; +import com.github.chistousov.authorization_backend.models.AcceptLoginRequestModelBuilder; +import com.github.chistousov.authorization_backend.models.CreateClientModel; +import com.github.chistousov.authorization_backend.models.ErrorModelBuilder; +import com.github.chistousov.authorization_backend.models.SessionForTokenModelBuilder; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@Testcontainers +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +public class OryHydraServiceTest { + + /*------- const (begin) ------- */ + + // path docker compose + private static final File pathToResourceDockerComposeFile = Path.of("").toAbsolutePath().getParent().getParent() + .resolve("docker-compose.yaml").toFile(); + + // service name in docker compose + private static final String hydraContainerDockerCompose = "ory-hydra-oauth2-example-authorization-server-hydra"; + // port admin Ory Hydra + private static final int hydraAdminPortDockerCompose = 4445; + // port public Ory Hydra + private static final int hydraPublicPortDockerCompose = 4444; + + // subject (login) resource owner + private static final String login = "some_login"; + + // domain Ory Hydra + private static final String AUTH_DOMAIN = "authorization-server.com"; + + // session after authentication and authorization + private static final String OAUTH2_AUTHENTICATION_SESSION = "ory_hydra_session"; + + private static final String LOGIN_CHALLENGE = "login_challenge"; + private static final String CONSENT_CHALLENGE = "consent_challenge"; + private static final String CODE_CHALLENGE = "code_challenge"; + private static final String LOGOUT_CHALLENGE = "logout_challenge"; + + // PKCE + private static final String CODE_CHALLENGE_METHOD = "code_challenge_method"; + // protocol PKCE code challenge method SHA256 + private static final String codeChallengeMethod = "S256"; + + // generated application test client + private static String scopes = "offine openid read"; + private static String redirectUri = "http://127.0.0.1:9631/callback"; + + /*------- const (end) ------- */ + + // start docker compose + @Container + public static DockerComposeContainer containersDockerCompose = new DockerComposeContainer<>( + pathToResourceDockerComposeFile) + // .env docker compose + .withEnv("USER_DATA_POSTGRESQL_PASSWORD", "superpass") + .withEnv("HYDRA_POSTGRESQL_PASSWORD", "superpass2") + .withEnv("HYDRA_SECRETS_COOKIE", "some_cookies111111111111111122222") + .withEnv("HYDRA_SECRETS_SYSTEM", "some_secrets111111111111111122222") + .withEnv("HYDRA_DEPENDS_ON_MIGRATE", "service_started") + // public Ory Hydra + .withExposedService(hydraContainerDockerCompose, hydraPublicPortDockerCompose, + Wait.forListeningPort().withStartupTimeout(Duration.ofMinutes(30))) + // admin Ory Hydra + .withExposedService(hydraContainerDockerCompose, hydraAdminPortDockerCompose, + Wait.forHttp("/version").withHeader("X-Forwarded-Proto", "https") + .withStartupTimeout(Duration.ofMinutes(30))) + // .yaml v2 <-> v3 + .withOptions("--compatibility") + // docker-compose local + .withLocalCompose(true); + + // http client for public Ory Hydra + private static WebTestClient publicWebTestClient; + + // OAuth 2.0 client Id + private static String clientId; + + @DynamicPropertySource + public static void setting(DynamicPropertyRegistry registry) throws UnsupportedEncodingException { + + /* postgresql user */ + + registry.add("spring.datasource.driverClassName", () -> "org.postgresql.Driver"); + registry.add("spring.jpa.properties.hibernate.dialect", () -> "org.hibernate.dialect.PostgreSQLDialect"); + registry.add("spring.datasource.url", () -> "1"); + + /* Ory Hydra admin */ + + var hydraAdminBaseURI = String.format("http://%s:%d/admin/", + containersDockerCompose.getServiceHost(hydraContainerDockerCompose, hydraAdminPortDockerCompose), + containersDockerCompose.getServicePort(hydraContainerDockerCompose, hydraAdminPortDockerCompose)); + + registry.add("application.ory-hydra.admin.baseURI", () -> hydraAdminBaseURI); + + WebTestClient adminWebTestClient = WebTestClient.bindToServer() + .baseUrl(hydraAdminBaseURI) + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + // Since Ory Hydra is configured for TLS termination, the header below is + // required + // We pretend that WebClient is a proxy with TLS termination + // Так как Ory Hydra настроена на TLS termination, то необходим заголовок ниже + // Делаем вид что, WebClient прокси с TLS termination + .defaultHeader("X-Forwarded-Proto", "https") + .build(); + + /* Ory Hydra Public */ + + var hydraPublicBaseURI = String.format("http://%s:%d/", + containersDockerCompose.getServiceHost(hydraContainerDockerCompose, hydraPublicPortDockerCompose), + containersDockerCompose.getServicePort(hydraContainerDockerCompose, hydraPublicPortDockerCompose)); + + registry.add("application.ory-hydra.public.baseURI", () -> hydraPublicBaseURI); + + publicWebTestClient = WebTestClient.bindToServer() + .baseUrl(hydraPublicBaseURI) + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + // Since Ory Hydra is configured for TLS termination, the header below is + // required + // We pretend that WebClient is a proxy with TLS termination + // Так как Ory Hydra настроена на TLS termination, то необходим заголовок ниже + // Делаем вид что, WebClient прокси с TLS termination + .defaultHeader("X-Forwarded-Proto", "https") + .build(); + + // create a test client application + CreateClientModel createClientModel = new CreateClientModel(); + createClientModel.setRedirect_uris(List.of(redirectUri)); + createClientModel.setGrant_types(List.of("authorization_code")); + createClientModel.setResponse_types(List.of("code")); + createClientModel.setScope(scopes); + + adminWebTestClient + .post() + .uri("/clients") + .body(Mono.just(createClientModel), CreateClientModel.class) + .exchange() + .expectStatus() + .isCreated() + .expectBody(CreateClientModel.class) + .value(val -> { + clientId = val.getClient_id(); + }); + + } + + @Autowired + private OryHydraService oryHydraService; + + /* ------- temporary variables (begin) --------- */ + + // state для 4.1.1 Authorization Request для grant type Authorization Code Grant + private String state; + private String nonce; + + private MultiValueMap cookiesBeforeOauth2LoginRedirect; + private MultiValueMap cookiesBeforeOauth2ConsentRedirect; + private String oauth2AuthenticationSession; + + private String loginLocation; + private String loginChallenge; + private String consentChallenge; + + private String redirectConsentLocation; + private String consentLocation; + private String redirectForContentVerifier; + + private String logoutLocation; + private String logoutChallenge; + private String redirectLogoutLocation; + + private String codeVerifier; + private String codeChallenge; + + /* ------- temporary variables (end) --------- */ + + @BeforeEach + public void initEach() throws UnsupportedEncodingException, NoSuchAlgorithmException { + + // PKCE + codeVerifier = SupportModule.generateCodeVerifier(); + codeChallenge = SupportModule.generateCodeChallange(codeVerifier); + + state = SupportModule.generateRandomStr(); + nonce = SupportModule.generateRandomStr(); + + cookiesBeforeOauth2LoginRedirect = null; + cookiesBeforeOauth2ConsentRedirect = null; + oauth2AuthenticationSession = null; + + loginLocation = null; + loginChallenge = null; + consentChallenge = null; + + redirectConsentLocation = null; + consentLocation = null; + redirectForContentVerifier = null; + + logoutLocation = null; + logoutChallenge = null; + redirectLogoutLocation = null; + + } + + /** + *

+ * Init Ory Hydra Login Flow. Get loginChallenge. + *

+ * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + private void oryHydraInitLoginFlow() throws URISyntaxException, UnsupportedEncodingException { + + String scopesURI = URLEncoder.encode(String.join(" ", scopes), "UTF-8"); + + publicWebTestClient + .get() + .uri(uriBuilder -> uriBuilder + .path("oauth2/auth") + .queryParam("client_id", clientId) + .queryParam("response_type", "code") + .queryParam("state", state) + .queryParam("nonce", nonce) + .queryParam("redirect_uri", redirectUri) + .queryParam("scope", scopesURI) + // PKCE + .queryParam(CODE_CHALLENGE, codeChallenge) + .queryParam(CODE_CHALLENGE_METHOD, codeChallengeMethod) + .build()) + .cookie(OAUTH2_AUTHENTICATION_SESSION, oauth2AuthenticationSession == null + || oauth2AuthenticationSession.isBlank() + || oauth2AuthenticationSession.isEmpty() ? "" + : oauth2AuthenticationSession) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", String.format("^https:\\/\\/%s\\/api\\/login\\?login_challenge=.+$", AUTH_DOMAIN)) + .expectHeader() + .value("Location", location -> loginLocation = location) + .expectHeader() + .exists("Set-Cookie") + .expectHeader() + .values("Set-Cookie", cookies -> { + cookiesBeforeOauth2LoginRedirect = new LinkedMultiValueMap<>(cookies.size()); + + for (int i = 0; i < cookies.size(); i++) { + String nameAndValueCookieStr = cookies.get(i).split(";")[0]; + + String[] nameAndValueCookieArray = nameAndValueCookieStr.split("=", 2); + + cookiesBeforeOauth2LoginRedirect.add(nameAndValueCookieArray[0], nameAndValueCookieArray[1]); + + } + + }); + + URI loginLocationURI = new URI(loginLocation); + String queryParamsStr = loginLocationURI.getQuery(); + + // check that the request parameters are present + assertThat(queryParamsStr).isNotEmpty().isNotBlank().contains("="); + + // parse request parameters into a convenient structure + var queryParams = Arrays + .stream(queryParamsStr.split("&")) + .map(SupportModule::splitQueryParameter) + .collect( + Collectors.groupingBy( + SimpleImmutableEntry::getKey, + LinkedHashMap::new, + Collectors.mapping(Map.Entry::getValue, + Collectors.toList()))); + + // check that there is a login_challenge in the request parameters + assertThat(queryParams).isNotEmpty().containsOnlyKeys(LOGIN_CHALLENGE); + + // remember login_challenge + loginChallenge = queryParams.get(LOGIN_CHALLENGE).get(0); + } + + /** + *

+ * Init Ory Hydra Consent Flow. Get loginChallenge. + *

+ * + * @param queryParamsStrRedirectConsent - query params for init Consent Flow + * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + private void oryHydraInitConsentFlow(String queryParamsStrRedirectConsent) throws URISyntaxException { + + publicWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/oauth2/auth") + .query(queryParamsStrRedirectConsent) + .build()) + .cookies(cookies -> cookies.addAll(cookiesBeforeOauth2LoginRedirect)) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", String.format("^https:\\/\\/%s\\/api\\/consent\\?consent_challenge=.+$", AUTH_DOMAIN)) + .expectHeader() + .value("Location", location -> consentLocation = location) + .expectCookie() + // remember ora hydra session for re-authentication + // (authentication without password re-entry) + // запоминаем сессию ora hydra для переаутентификации + // (аутентификации без повторного ввода пароля) + .value(OAUTH2_AUTHENTICATION_SESSION, val -> oauth2AuthenticationSession = val) + .expectHeader() + .exists("Set-Cookie") + .expectHeader() + .values("Set-Cookie", cookies -> { + cookiesBeforeOauth2ConsentRedirect = new LinkedMultiValueMap<>(cookies.size()); + + for (int i = 0; i < cookies.size(); i++) { + String nameAndValueCookieStr = cookies.get(i).split(";")[0]; + + String[] nameAndValueCookieArray = nameAndValueCookieStr.split("=", 2); + + if (cookiesBeforeOauth2ConsentRedirect.containsKey(nameAndValueCookieArray[0])) { + cookiesBeforeOauth2ConsentRedirect.remove(nameAndValueCookieArray[0]); + cookiesBeforeOauth2ConsentRedirect.add(nameAndValueCookieArray[0], nameAndValueCookieArray[1]); + } else { + cookiesBeforeOauth2ConsentRedirect.add(nameAndValueCookieArray[0], nameAndValueCookieArray[1]); + } + + } + + }); + + URI consentLocationURI = new URI(consentLocation); + var queryParamsStrConsent = consentLocationURI.getQuery(); + + // check that the request parameters are present + assertThat(queryParamsStrConsent).isNotEmpty().isNotBlank().contains("="); + + // parse request parameters into a convenient structure + var queryParamsConsent = Arrays + .stream(queryParamsStrConsent.split("&")) + .map(SupportModule::splitQueryParameter) + .collect( + Collectors.groupingBy( + SimpleImmutableEntry::getKey, + LinkedHashMap::new, + Collectors.mapping(Map.Entry::getValue, + Collectors.toList()))); + + // check that there is consent_challenge in the request parameters + assertThat(queryParamsConsent).isNotEmpty().containsOnlyKeys(CONSENT_CHALLENGE); + + // remember consent_challenge + consentChallenge = queryParamsConsent.get(CONSENT_CHALLENGE).get(0); + } + + /** + *

+ * Ory Hydra full Cycle Auth + *

+ * + * @author Nikita Chistousov (chistousov.nik@yandex.ru) + * @since 11 + */ + private void fullCycleAuth() throws UnsupportedEncodingException, NoSuchAlgorithmException, URISyntaxException { + + // 4.1.1 Authorization Request + + // authentication request + oryHydraInitLoginFlow(); + + /* BEGIN ory hydra login flow */ + + // check info loginChallenge + StepVerifier + .create(this.oryHydraService.loginRequestInfo(loginChallenge)) + .expectNextMatches(body -> body.getClient().getClientId().equals(clientId)) + .verifyComplete(); + + // authorization with redirect + var acceptLoginRequestModel = Mono.just( + AcceptLoginRequestModelBuilder.builder() + .setSubject(login) + .setRemember(true) + .setRememberFor(60L * 60L * 24L) + .setContextModel("") + .build()); + StepVerifier + .create(this.oryHydraService.acceptLoginRequest(loginChallenge, acceptLoginRequestModel)) + .expectNextMatches( + responseWithRedirectModel -> { + + redirectConsentLocation = responseWithRedirectModel.getRedirectTo(); + + return redirectConsentLocation + .matches( + String.format( + "^https:\\/\\/%s\\/oauth2\\/auth\\?client_id=%s&code_challenge=%s&code_challenge_method=%s&login_verifier=.+&response_type=code&scope=.+&state=%s$", + AUTH_DOMAIN, clientId, codeChallenge, codeChallengeMethod, state)); + }) + .verifyComplete(); + + /* END ory hydra login flow */ + + URI redirectConsentLocationURI = new URI(redirectConsentLocation); + var queryParamsStrRedirectConsent = redirectConsentLocationURI.getQuery(); + + // remember the login flow state and switch to ory hydra consent flow + oryHydraInitConsentFlow(queryParamsStrRedirectConsent); + + /* BEGIN ory hydra consent flow */ + + StepVerifier + .create(this.oryHydraService.consentRequestInfo(consentChallenge)) + .expectNextMatches(body -> body.getClient().getClientId().equals(clientId)) + .verifyComplete(); + + TestAccessTokenExtension accessTokenExtension = new TestAccessTokenExtension(); + accessTokenExtension.setId(1L); + accessTokenExtension.setName("some_name"); + + TestIdTokenExtension idTokenExtension = new TestIdTokenExtension(); + idTokenExtension.setHash("some_hash"); + + var acceptConsentRequestModelMono = Mono.just( + AcceptConsentRequestModelBuilder.builder() + .setGrantScope(Arrays.asList(scopes.split(" "))) + .setRemember(true) + .setRememberFor(36L * 60L * 60L) + .setSession( + SessionForTokenModelBuilder + .builder() + .setAccessTokenExtension(accessTokenExtension) + .setIdTokenExtension(idTokenExtension) + .build()) + .build()); + + StepVerifier + .create(this.oryHydraService.acceptConsentRequest(consentChallenge, acceptConsentRequestModelMono)) + .expectNextMatches( + responseWithRedirectModel -> { + + redirectForContentVerifier = responseWithRedirectModel.getRedirectTo(); + + return redirectForContentVerifier + .matches( + String.format( + "^https:\\/\\/%s\\/oauth2\\/auth\\?client_id=%s&code_challenge=%s&code_challenge_method=%s&consent_verifier=.+&response_type=code&scope=.+&state=%s$", + AUTH_DOMAIN, clientId, codeChallenge, codeChallengeMethod, state)); + }) + .verifyComplete(); + + URI redirectForContentVerifierURI = new URI(redirectForContentVerifier); + var queryParamsForContentVerifier = redirectForContentVerifierURI.getQuery(); + + /* END ory hydra consent flow */ + + // THE END + publicWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/oauth2/auth") + .query(queryParamsForContentVerifier) + .build()) + .cookies(cookies -> { + cookies.addAll(cookiesBeforeOauth2ConsentRedirect); + cookiesBeforeOauth2LoginRedirect.entrySet() + .removeIf(k -> cookiesBeforeOauth2ConsentRedirect.containsKey(k.getKey())); + cookies.addAll(cookiesBeforeOauth2LoginRedirect); + cookies.add(OAUTH2_AUTHENTICATION_SESSION, oauth2AuthenticationSession); + }) + .exchange() + .expectStatus() + .isSeeOther() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", String.format("^%s\\?code=.+&scope=.+&state=%s$", redirectUri, state)); + } + + @Test + @Order(1) + @DisplayName("Full cycle of user authentication and authorization") + void fullCycleAuthentication() throws UnsupportedEncodingException, NoSuchAlgorithmException, URISyntaxException { + fullCycleAuth(); + } + + @Test + @Order(2) + @DisplayName("Reject login flow") + void rejectLoginRequest() throws URISyntaxException, UnsupportedEncodingException { + + // authentication request + oryHydraInitLoginFlow(); + + // error for reject + var errorModel = ErrorModelBuilder + .builder() + .setError("some_error") + .setErrorDescription("some_description") + .setStatusCode(504L) + .setErrorDebug("some_debug") + .setErrorHint("hint") + .build(); + var errorModelMono = Mono.just(errorModel); + + StepVerifier + .create(this.oryHydraService.rejectLoginRequest(loginChallenge, errorModelMono)) + .expectNextMatches( + responseWithRedirectModel -> { + + redirectConsentLocation = responseWithRedirectModel.getRedirectTo(); + + return redirectConsentLocation + .matches( + String.format( + "^https:\\/\\/%s\\/oauth2\\/auth\\?client_id=%s&code_challenge=%s&code_challenge_method=%s&login_verifier=.+&response_type=code&scope=.+&state=%s$", + AUTH_DOMAIN, clientId, codeChallenge, codeChallengeMethod, state)); + }) + .verifyComplete(); + + URI redirectConsentLocationURI = new URI(redirectConsentLocation); + var queryParamsStrRedirectConsent = redirectConsentLocationURI.getQuery(); + + publicWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/oauth2/auth") + .query(queryParamsStrRedirectConsent) + .build()) + .cookies(cookies -> cookies.addAll(cookiesBeforeOauth2LoginRedirect)) + .exchange() + .expectStatus() + .isSeeOther() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", + String.format("^%s\\?error=%s&error_description=%s\\+hint&state=%s$", + redirectUri, errorModel.getError(), errorModel.getErrorDescription(), state)); + } + + @Test + @Order(3) + @DisplayName("Reject consent flow") + void rejectConsentRequest() throws URISyntaxException, UnsupportedEncodingException { + + oryHydraInitLoginFlow(); + + /* BEGIN ory hydra login flow */ + + var acceptLoginRequestModel = Mono.just( + AcceptLoginRequestModelBuilder.builder() + .setSubject(login) + .setRemember(true) + .setRememberFor(60L * 60L * 24L) + .setContextModel("") + .build()); + + StepVerifier + .create(this.oryHydraService.acceptLoginRequest(loginChallenge, acceptLoginRequestModel)) + .expectNextMatches( + responseWithRedirectModel -> { + + redirectConsentLocation = responseWithRedirectModel.getRedirectTo(); + + return redirectConsentLocation + .matches( + String.format( + "^https:\\/\\/%s\\/oauth2\\/auth\\?client_id=%s&code_challenge=%s&code_challenge_method=%s&login_verifier=.+&response_type=code&scope=.+&state=%s$", + AUTH_DOMAIN, clientId, codeChallenge, codeChallengeMethod, state)); + }) + .verifyComplete(); + + /* END ory hydra login flow */ + + URI redirectConsentLocationURI = new URI(redirectConsentLocation); + var queryParamsStrRedirectConsent = redirectConsentLocationURI.getQuery(); + + oryHydraInitConsentFlow(queryParamsStrRedirectConsent); + + /* BEGIN ory hydra consent flow */ + + var errorModel = ErrorModelBuilder + .builder() + .setError("some_error") + .setErrorDescription("some_description") + .setStatusCode(504L) + .build(); + + var errorModelMono = Mono.just(errorModel); + + StepVerifier + .create(this.oryHydraService.rejectConsentRequest(consentChallenge, errorModelMono)) + .expectNextMatches( + responseWithRedirectModel -> { + + redirectForContentVerifier = responseWithRedirectModel.getRedirectTo(); + + return redirectForContentVerifier + .matches( + String.format( + "^https:\\/\\/%s\\/oauth2\\/auth\\?client_id=%s&code_challenge=%s&code_challenge_method=%s&consent_verifier=.+&response_type=code&scope=.+&state=%s$", + AUTH_DOMAIN, clientId, codeChallenge, codeChallengeMethod, state)); + }) + .verifyComplete(); + + URI redirectForContentVerifierURI = new URI(redirectForContentVerifier); + var queryParamsForContentVerifier = redirectForContentVerifierURI.getQuery(); + + /* END ory hydra consent flow */ + + publicWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/oauth2/auth") + .query(queryParamsForContentVerifier) + .build()) + .cookies(cookies -> { + cookies.addAll(cookiesBeforeOauth2ConsentRedirect); + cookiesBeforeOauth2LoginRedirect.entrySet() + .removeIf(k -> cookiesBeforeOauth2ConsentRedirect.containsKey(k.getKey())); + cookies.addAll(cookiesBeforeOauth2LoginRedirect); + cookies.add(OAUTH2_AUTHENTICATION_SESSION, oauth2AuthenticationSession); + }) + .exchange() + .expectStatus() + .isSeeOther() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", + String.format("^%s\\?error=%s&error_description=%s&state=%s$", + redirectUri, errorModel.getError(), errorModel.getErrorDescription(), state)); + } + + @Test + @Order(4) + @DisplayName("Full exit cycle") + void fullCycleLogout() + throws URISyntaxException, UnsupportedEncodingException, NoSuchAlgorithmException { + + fullCycleAuth(); + + /* BEGIN ory hydra logout flow */ + + publicWebTestClient + .get() + .uri(uriBuilder -> uriBuilder + .path("oauth2/sessions/logout") + .build()) + .cookie(OAUTH2_AUTHENTICATION_SESSION, oauth2AuthenticationSession) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", + String.format("^https:\\/\\/%s\\/api\\/logout\\?logout_challenge=.+$", + AUTH_DOMAIN)) + .expectHeader() + .value("Location", location -> logoutLocation = location); + + URI logoutLocationURI = new URI(logoutLocation); + String queryParamsStr = logoutLocationURI.getQuery(); + + var queryParams = Arrays + .stream(queryParamsStr.split("&")) + .map(SupportModule::splitQueryParameter) + .collect( + Collectors.groupingBy( + SimpleImmutableEntry::getKey, + LinkedHashMap::new, + Collectors.mapping(Map.Entry::getValue, + Collectors.toList()))); + + logoutChallenge = queryParams.get(LOGOUT_CHALLENGE).get(0); + + StepVerifier + .create(this.oryHydraService.logoutRequestInfo(logoutChallenge)) + .expectNextMatches(body -> body.getSubject().equals(login)) + .verifyComplete(); + + StepVerifier + .create(this.oryHydraService.acceptLogoutRequest(logoutChallenge)) + .expectNextMatches( + responseWithRedirectModel -> { + + redirectLogoutLocation = responseWithRedirectModel.getRedirectTo(); + + return redirectLogoutLocation + .matches( + String.format( + "^https:\\/\\/%s\\/oauth2\\/sessions\\/logout\\?logout_verifier=.+$", + AUTH_DOMAIN)); + }) + .verifyComplete(); + + URI redirectForLogoutVerifierURI = new URI(redirectLogoutLocation); + var queryParamsForLogoutVerifier = redirectForLogoutVerifierURI.getQuery(); + + publicWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("oauth2/sessions/logout") + .query(queryParamsForLogoutVerifier) + .build()) + .cookie(OAUTH2_AUTHENTICATION_SESSION, oauth2AuthenticationSession) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", + String.format("^https:\\/\\/%s\\/logout\\/success$", + AUTH_DOMAIN)); + + /* END ory hydra logout flow */ + } + + @Test + @Order(5) + @DisplayName("reject Logout Flow") + void rejectLogoutRequest() + throws URISyntaxException, UnsupportedEncodingException, NoSuchAlgorithmException { + + fullCycleAuth(); + + /* BEGIN ory hydra logout flow */ + + publicWebTestClient + .get() + .uri(uriBuilder -> uriBuilder + .path("oauth2/sessions/logout") + .build()) + .cookie(OAUTH2_AUTHENTICATION_SESSION, oauth2AuthenticationSession) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", + String.format("^https:\\/\\/%s\\/api\\/logout\\?logout_challenge=.+$", + AUTH_DOMAIN)) + .expectHeader() + .value("Location", location -> logoutLocation = location); + + URI logoutLocationURI = new URI(logoutLocation); + String queryParamsStr = logoutLocationURI.getQuery(); + + var queryParams = Arrays + .stream(queryParamsStr.split("&")) + .map(SupportModule::splitQueryParameter) + .collect( + Collectors.groupingBy( + SimpleImmutableEntry::getKey, + LinkedHashMap::new, + Collectors.mapping(Map.Entry::getValue, + Collectors.toList()))); + + logoutChallenge = queryParams.get(LOGOUT_CHALLENGE).get(0); + + StepVerifier + .create(this.oryHydraService.logoutRequestInfo(logoutChallenge)) + .expectNextMatches(body -> body.getSubject().equals(login)) + .verifyComplete(); + + StepVerifier + .create(this.oryHydraService.rejectLogoutRequest(logoutChallenge)) + .verifyComplete(); + + /* END ory hydra logout flow */ + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/services/UserServiceTest.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/services/UserServiceTest.java new file mode 100644 index 0000000..8304244 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/java/com/github/chistousov/authorization_backend/services/UserServiceTest.java @@ -0,0 +1,249 @@ +package com.github.chistousov.authorization_backend.services; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.nio.file.Path; +import java.time.Duration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.DockerComposeContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import com.github.chistousov.authorization_backend.dao.entities.User; +import com.github.chistousov.authorization_backend.exceptions.IncorrectPasswordException; +import com.github.chistousov.authorization_backend.exceptions.LoginDoesNotExistException; +import com.github.chistousov.authorization_backend.models.PostRegistrationModel; + +import reactor.test.StepVerifier; + +@Testcontainers +@SpringBootTest +public class UserServiceTest { + + /*------- const (begin) ------- */ + + // path docker compose + private static final File pathToResourceDockerComposeFile = Path.of("").toAbsolutePath().getParent().getParent() + .resolve("docker-compose.yaml").toFile(); + + // postgresql service in docker compose + private static final String postgresqlUserDataContainerDockerCompose = "ory-hydra-oauth2-ex-auth-server-userdata-postgresql"; + private static final int postgresqlUserDataPortDockerCompose = 5432; + private static final String postgresqlUserDataUserDockerCompose = "user_data"; + private static final String postgresqlUserDataPassDockerCompose = "superpass"; + + /*------- const (end) ------- */ + + // start docker compose + @Container + public static DockerComposeContainer containersDockerCompose = new DockerComposeContainer<>( + pathToResourceDockerComposeFile) + // .env docker compose + .withEnv("USER_DATA_POSTGRESQL_PASSWORD", postgresqlUserDataPassDockerCompose) + .withEnv("HYDRA_POSTGRESQL_PASSWORD", "superpass2") + .withEnv("HYDRA_SECRETS_COOKIE", "some_cookies111111111111111122222") + .withEnv("HYDRA_SECRETS_SYSTEM", "some_secrets111111111111111122222") + .withEnv("HYDRA_DEPENDS_ON_MIGRATE", "service_started") + // open postgresql port + .withExposedService(postgresqlUserDataContainerDockerCompose, postgresqlUserDataPortDockerCompose, + Wait.forSuccessfulCommand( + String.format("pg_isready -U %s", postgresqlUserDataUserDockerCompose)) + .withStartupTimeout(Duration.ofMinutes(1))) + // .yaml v2 <-> v3 + .withOptions("--compatibility") + // docker-compose local + .withLocalCompose(true); + + @DynamicPropertySource + public static void setting(DynamicPropertyRegistry registry) + throws UnsupportedEncodingException { + + registry.add("spring.datasource.driverClassName", () -> "org.postgresql.Driver"); + registry.add("spring.jpa.properties.hibernate.dialect", () -> "org.hibernate.dialect.PostgreSQLDialect"); + + String postgresqlStringConn = String.format( + "jdbc:postgresql://%s:%d/%s?user=%s&password=%s&escapeSyntaxCallMode=callIfNoReturn", + containersDockerCompose.getServiceHost(postgresqlUserDataContainerDockerCompose, + postgresqlUserDataPortDockerCompose), + containersDockerCompose.getServicePort(postgresqlUserDataContainerDockerCompose, + postgresqlUserDataPortDockerCompose), + postgresqlUserDataUserDockerCompose, + postgresqlUserDataUserDockerCompose, + postgresqlUserDataPassDockerCompose); + + registry.add("spring.datasource.url", () -> postgresqlStringConn); + + registry.add("application.ory-hydra.admin.baseURI", () -> ""); + registry.add("application.ory-hydra.public.baseURI", () -> ""); + } + + @Autowired + private UserService userService; + + @Test + @DisplayName("Successful user creation and attempt to re-create it") + void testCreateUser() { + // given (instead of when) + + final String login1 = "someuser1"; + final String login2 = "someuser2"; + + final String pass = "asdasfdasffsadfa"; + + final String org1 = "someorg1"; + final String org2 = "someorg2"; + + final PostRegistrationModel user1 = PostRegistrationModel + .builder() + .login(login1) + .password(pass) + .orgName(org1) + .build(); + + final PostRegistrationModel user2 = PostRegistrationModel + .builder() + .login(login1) + .password(pass) + .orgName(org2) + .build(); + + final PostRegistrationModel user3 = PostRegistrationModel + .builder() + .login(login2) + .password(pass) + .orgName(org1) + .build(); + + final PostRegistrationModel user4 = PostRegistrationModel + .builder() + .login(login2) + .password(pass) + .orgName(org2) + .build(); + + // when + StepVerifier + .create(userService.createUser(user1)) + .expectNextCount(1) + .verifyComplete(); + + StepVerifier + .create(userService.createUser(user2)) + .verifyError(); + + StepVerifier + .create(userService.createUser(user3)) + .verifyError(); + + StepVerifier + .create(userService.createUser(user4)) + .expectNextCount(1) + .verifyComplete(); + + // then (instead of verify) + + } + + @Test + @DisplayName("Check user") + void testGetUserAndCheck() { + + // given (instead of when) + + final String login1 = "someuser1_"; + final String login2 = "someuser2_"; + + final String pass1 = "zxcvxbxvvcxvxcvxcv"; + final String pass2 = "jdfglkvjbjbjbj"; + + final String org1 = "someorg1_"; + + final PostRegistrationModel user = PostRegistrationModel + .builder() + .login(login1) + .password(pass1) + .orgName(org1) + .build(); + + final User expectedUser = User.builder() + .login(login1) + .password("") + .orgName(org1) + .build(); + + StepVerifier + .create(userService.createUser(user)) + .expectNextCount(1) + .verifyComplete(); + + // when + + StepVerifier + .create(userService.getUserAndCheck(user.getLogin(), user.getPassword())) + .expectNext(expectedUser) + .verifyComplete(); + + StepVerifier + .create(userService.getUserAndCheck(login2, pass1)) + .verifyErrorMatches(ex -> ex instanceof LoginDoesNotExistException + && ex.getMessage().equals("login does not exist")); + + StepVerifier + .create(userService.getUserAndCheck(login1, pass2)) + .verifyErrorMatches(ex -> ex instanceof IncorrectPasswordException + && ex.getMessage().equals("Password is incorrect! ")); + + } + + @Test + @DisplayName("Get user") + void testGetUser() { + + // given (instead of when) + + final String login1 = "someuser1_czxc"; + final String login2 = "someuser2_czx"; + + final String pass1 = "zxcvxbxvvcxvxcvxcvccc"; + + final String org1 = "someorg1_adsad"; + + final PostRegistrationModel user = PostRegistrationModel + .builder() + .login(login1) + .password(pass1) + .orgName(org1) + .build(); + + final User expectedUser = User.builder() + .login(login1) + .password("") + .orgName(org1) + .build(); + + StepVerifier + .create(userService.createUser(user)) + .expectNextCount(1) + .verifyComplete(); + + // when + + StepVerifier + .create(userService.getUser(user.getLogin())) + .expectNext(expectedUser) + .verifyComplete(); + + StepVerifier + .create(userService.getUser(login2)) + .verifyErrorMatches(ex -> ex instanceof LoginDoesNotExistException + && ex.getMessage().equals("login does not exist")); + + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/resources/ory-hydra-open-api-v3.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/resources/ory-hydra-open-api-v3.json new file mode 100644 index 0000000..f812412 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/src/test/resources/ory-hydra-open-api-v3.json @@ -0,0 +1,3689 @@ +{ + "components": { + "responses": { + "emptyResponse": { + "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is\ntypically 201." + }, + "errorOAuth2BadRequest": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "Bad Request Error Response" + }, + "errorOAuth2Default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "Default Error Response" + }, + "errorOAuth2NotFound": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "Not Found Error Response" + }, + "listOAuth2Clients": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/oAuth2Client" + }, + "type": "array" + } + } + }, + "description": "Paginated OAuth2 Client List Response" + } + }, + "schemas": { + "DefaultError": {}, + "JSONRawMessage": { + "title": "JSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger." + }, + "NullBool": { + "nullable": true, + "type": "boolean" + }, + "NullDuration": { + "description": "Specify a time duration in milliseconds, seconds, minutes, hours.", + "pattern": "^([0-9]+(ns|us|ms|s|m|h))*$", + "title": "Time duration", + "type": "string" + }, + "NullInt": { + "nullable": true, + "type": "integer" + }, + "NullString": { + "nullable": true, + "type": "string" + }, + "NullTime": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "NullUUID": { + "format": "uuid4", + "nullable": true, + "type": "string" + }, + "StringSliceJSONFormat": { + "items": { + "type": "string" + }, + "title": "StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.", + "type": "array" + }, + "Time": { + "format": "date-time", + "type": "string" + }, + "UUID": { + "format": "uuid4", + "type": "string" + }, + "acceptOAuth2ConsentRequest": { + "properties": { + "grant_access_token_audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "grant_scope": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "handled_at": { + "$ref": "#/components/schemas/nullTime" + }, + "remember": { + "description": "Remember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same\nclient asks the same user for the same, or a subset of, scope.", + "type": "boolean" + }, + "remember_for": { + "description": "RememberFor sets how long the consent authorization should be remembered for in seconds. If set to `0`, the\nauthorization will be remembered indefinitely.", + "format": "int64", + "type": "integer" + }, + "session": { + "$ref": "#/components/schemas/acceptOAuth2ConsentRequestSession" + } + }, + "title": "The request payload used to accept a consent request.", + "type": "object" + }, + "acceptOAuth2ConsentRequestSession": { + "properties": { + "access_token": { + "description": "AccessToken sets session data for the access and refresh token, as well as any future tokens issued by the\nrefresh grant. Keep in mind that this data will be available to anyone performing OAuth 2.0 Challenge Introspection.\nIf only your services can perform OAuth 2.0 Challenge Introspection, this is usually fine. But if third parties\ncan access that endpoint as well, sensitive data from the session might be exposed to them. Use with care!" + }, + "id_token": { + "description": "IDToken sets session data for the OpenID Connect ID token. Keep in mind that the session'id payloads are readable\nby anyone that has access to the ID Challenge. Use with care!" + } + }, + "title": "Pass session data to a consent request.", + "type": "object" + }, + "acceptOAuth2LoginRequest": { + "properties": { + "acr": { + "description": "ACR sets the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it\nto express that, for example, a user authenticated using two factor authentication.", + "type": "string" + }, + "amr": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "context": { + "$ref": "#/components/schemas/JSONRawMessage" + }, + "extend_session_lifespan": { + "description": "Extend OAuth2 authentication session lifespan\n\nIf set to `true`, the OAuth2 authentication cookie lifespan is extended. This is for example useful if you want the user to be able to use `prompt=none` continuously.\n\nThis value can only be set to `true` if the user has an authentication, which is the case if the `skip` value is `true`.", + "type": "boolean" + }, + "force_subject_identifier": { + "description": "ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the\n(Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID\nConnect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client.\n\nPlease note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the\nsub claim in the OAuth 2.0 Introspection.\n\nPer default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself\nyou can use this field. Please note that setting this field has no effect if `pairwise` is not configured in\nORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's\nconfiguration).\n\nPlease also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies\nthat you have to compute this value on every authentication process (probably depending on the client ID or some\nother unique value).\n\nIf you fail to compute the proper value, then authentication processes which have id_token_hint set might fail.", + "type": "string" + }, + "remember": { + "description": "Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store\na cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she\nwill not be asked to log in again.", + "type": "boolean" + }, + "remember_for": { + "description": "RememberFor sets how long the authentication should be remembered for in seconds. If set to `0`, the\nauthorization will be remembered for the duration of the browser session (using a session cookie).", + "format": "int64", + "type": "integer" + }, + "subject": { + "description": "Subject is the user ID of the end-user that authenticated.", + "type": "string" + } + }, + "required": [ + "subject" + ], + "title": "HandledLoginRequest is the request payload used to accept a login request.", + "type": "object" + }, + "createJsonWebKeySet": { + "description": "Create JSON Web Key Set Request Body", + "properties": { + "alg": { + "description": "JSON Web Key Algorithm\n\nThe algorithm to be used for creating the key. Supports `RS256`, `ES256`, `ES512`, `HS512`, and `HS256`.", + "type": "string" + }, + "kid": { + "description": "JSON Web Key ID\n\nThe Key ID of the key to be created.", + "type": "string" + }, + "use": { + "description": "JSON Web Key Use\n\nThe \"use\" (public key use) parameter identifies the intended use of\nthe public key. The \"use\" parameter is employed to indicate whether\na public key is used for encrypting data or verifying the signature\non data. Valid values are \"enc\" and \"sig\".", + "type": "string" + } + }, + "required": [ + "alg", + "use", + "kid" + ], + "type": "object" + }, + "errorOAuth2": { + "description": "Error", + "properties": { + "error": { + "description": "Error", + "type": "string" + }, + "error_debug": { + "description": "Error Debug Information\n\nOnly available in dev mode.", + "type": "string" + }, + "error_description": { + "description": "Error Description", + "type": "string" + }, + "error_hint": { + "description": "Error Hint\n\nHelps the user identify the error cause.", + "example": "The redirect URL is not allowed.", + "type": "string" + }, + "status_code": { + "description": "HTTP Status Code", + "example": 401, + "format": "int64", + "type": "integer" + } + }, + "type": "object" + }, + "genericError": { + "properties": { + "code": { + "description": "The status code", + "example": 404, + "format": "int64", + "type": "integer" + }, + "debug": { + "description": "Debug information\n\nThis field is often not exposed to protect against leaking\nsensitive information.", + "example": "SQL field \"foo\" is not a bool.", + "type": "string" + }, + "details": { + "description": "Further error details" + }, + "id": { + "description": "The error ID\n\nUseful when trying to identify various errors in application logic.", + "type": "string" + }, + "message": { + "description": "Error message\n\nThe error's message.", + "example": "The resource could not be found", + "type": "string" + }, + "reason": { + "description": "A human-readable reason for the error", + "example": "User with ID 1234 does not exist.", + "type": "string" + }, + "request": { + "description": "The request ID\n\nThe request ID is often exposed internally in order to trace\nerrors across service architectures. This is often a UUID.", + "example": "d7ef54b1-ec15-46e6-bccb-524b82c035e6", + "type": "string" + }, + "status": { + "description": "The status description", + "example": "Not Found", + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "healthNotReadyStatus": { + "properties": { + "errors": { + "additionalProperties": { + "type": "string" + }, + "description": "Errors contains a list of errors that caused the not ready status.", + "type": "object" + } + }, + "type": "object" + }, + "healthStatus": { + "properties": { + "status": { + "description": "Status always contains \"ok\".", + "type": "string" + } + }, + "type": "object" + }, + "introspectedOAuth2Token": { + "description": "Introspection contains an access token's session data as specified by\n[IETF RFC 7662](https://tools.ietf.org/html/rfc7662)", + "properties": { + "active": { + "description": "Active is a boolean indicator of whether or not the presented token\nis currently active. The specifics of a token's \"active\" state\nwill vary depending on the implementation of the authorization\nserver and the information it keeps about its tokens, but a \"true\"\nvalue return for the \"active\" property will generally indicate\nthat a given token has been issued by this authorization server,\nhas not been revoked by the resource owner, and is within its\ngiven time window of validity (e.g., after its issuance time and\nbefore its expiration time).", + "type": "boolean" + }, + "aud": { + "description": "Audience contains a list of the token's intended audiences.", + "items": { + "type": "string" + }, + "type": "array" + }, + "client_id": { + "description": "ID is aclient identifier for the OAuth 2.0 client that\nrequested this token.", + "type": "string" + }, + "exp": { + "description": "Expires at is an integer timestamp, measured in the number of seconds\nsince January 1 1970 UTC, indicating when this token will expire.", + "format": "int64", + "type": "integer" + }, + "ext": { + "additionalProperties": {}, + "description": "Extra is arbitrary data set by the session.", + "type": "object" + }, + "iat": { + "description": "Issued at is an integer timestamp, measured in the number of seconds\nsince January 1 1970 UTC, indicating when this token was\noriginally issued.", + "format": "int64", + "type": "integer" + }, + "iss": { + "description": "IssuerURL is a string representing the issuer of this token", + "type": "string" + }, + "nbf": { + "description": "NotBefore is an integer timestamp, measured in the number of seconds\nsince January 1 1970 UTC, indicating when this token is not to be\nused before.", + "format": "int64", + "type": "integer" + }, + "obfuscated_subject": { + "description": "ObfuscatedSubject is set when the subject identifier algorithm was set to \"pairwise\" during authorization.\nIt is the `sub` value of the ID Token that was issued.", + "type": "string" + }, + "scope": { + "description": "Scope is a JSON string containing a space-separated list of\nscopes associated with this token.", + "type": "string" + }, + "sub": { + "description": "Subject of the token, as defined in JWT [RFC7519].\nUsually a machine-readable identifier of the resource owner who\nauthorized this token.", + "type": "string" + }, + "token_type": { + "description": "TokenType is the introspected token's type, typically `Bearer`.", + "type": "string" + }, + "token_use": { + "description": "TokenUse is the introspected token's use, for example `access_token` or `refresh_token`.", + "type": "string" + }, + "username": { + "description": "Username is a human-readable identifier for the resource owner who\nauthorized this token.", + "type": "string" + } + }, + "required": [ + "active" + ], + "type": "object" + }, + "jsonPatch": { + "description": "A JSONPatch document as defined by RFC 6902", + "properties": { + "from": { + "description": "This field is used together with operation \"move\" and uses JSON Pointer notation.\n\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).", + "example": "/name", + "type": "string" + }, + "op": { + "description": "The operation to be performed. One of \"add\", \"remove\", \"replace\", \"move\", \"copy\", or \"test\".", + "example": "replace", + "type": "string" + }, + "path": { + "description": "The path to the target path. Uses JSON pointer notation.\n\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).", + "example": "/name", + "type": "string" + }, + "value": { + "description": "The value to be used within the operations.\n\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).", + "example": "foobar" + } + }, + "required": [ + "op", + "path" + ], + "type": "object" + }, + "jsonPatchDocument": { + "description": "A JSONPatchDocument request", + "items": { + "$ref": "#/components/schemas/jsonPatch" + }, + "type": "array" + }, + "jsonWebKey": { + "properties": { + "alg": { + "description": "The \"alg\" (algorithm) parameter identifies the algorithm intended for\nuse with the key. The values used should either be registered in the\nIANA \"JSON Web Signature and Encryption Algorithms\" registry\nestablished by [JWA] or be a value that contains a Collision-\nResistant Name.", + "example": "RS256", + "type": "string" + }, + "crv": { + "example": "P-256", + "type": "string" + }, + "d": { + "example": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "type": "string" + }, + "dp": { + "example": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "type": "string" + }, + "dq": { + "example": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "type": "string" + }, + "e": { + "example": "AQAB", + "type": "string" + }, + "k": { + "example": "GawgguFyGrWKav7AX4VKUg", + "type": "string" + }, + "kid": { + "description": "The \"kid\" (key ID) parameter is used to match a specific key. This\nis used, for instance, to choose among a set of keys within a JWK Set\nduring key rollover. The structure of the \"kid\" value is\nunspecified. When \"kid\" values are used within a JWK Set, different\nkeys within the JWK Set SHOULD use distinct \"kid\" values. (One\nexample in which different keys might use the same \"kid\" value is if\nthey have different \"kty\" (key type) values but are considered to be\nequivalent alternatives by the application using them.) The \"kid\"\nvalue is a case-sensitive string.", + "example": "1603dfe0af8f4596", + "type": "string" + }, + "kty": { + "description": "The \"kty\" (key type) parameter identifies the cryptographic algorithm\nfamily used with the key, such as \"RSA\" or \"EC\". \"kty\" values should\neither be registered in the IANA \"JSON Web Key Types\" registry\nestablished by [JWA] or be a value that contains a Collision-\nResistant Name. The \"kty\" value is a case-sensitive string.", + "example": "RSA", + "type": "string" + }, + "n": { + "example": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "type": "string" + }, + "p": { + "example": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "type": "string" + }, + "q": { + "example": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "type": "string" + }, + "qi": { + "example": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "type": "string" + }, + "use": { + "description": "Use (\"public key use\") identifies the intended use of\nthe public key. The \"use\" parameter is employed to indicate whether\na public key is used for encrypting data or verifying the signature\non data. Values are commonly \"sig\" (signature) or \"enc\" (encryption).", + "example": "sig", + "type": "string" + }, + "x": { + "example": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "type": "string" + }, + "x5c": { + "description": "The \"x5c\" (X.509 certificate chain) parameter contains a chain of one\nor more PKIX certificates [RFC5280]. The certificate chain is\nrepresented as a JSON array of certificate value strings. Each\nstring in the array is a base64-encoded (Section 4 of [RFC4648] --\nnot base64url-encoded) DER [ITU.X690.1994] PKIX certificate value.\nThe PKIX certificate containing the key value MUST be the first\ncertificate.", + "items": { + "type": "string" + }, + "type": "array" + }, + "y": { + "example": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0", + "type": "string" + } + }, + "required": [ + "use", + "kty", + "kid", + "alg" + ], + "type": "object" + }, + "jsonWebKeySet": { + "description": "JSON Web Key Set", + "properties": { + "keys": { + "description": "List of JSON Web Keys\n\nThe value of the \"keys\" parameter is an array of JSON Web Key (JWK)\nvalues. By default, the order of the JWK values within the array does\nnot imply an order of preference among them, although applications\nof JWK Sets can choose to assign a meaning to the order for their\npurposes, if desired.", + "items": { + "$ref": "#/components/schemas/jsonWebKey" + }, + "type": "array" + } + }, + "type": "object" + }, + "nullDuration": { + "nullable": true, + "pattern": "^[0-9]+(ns|us|ms|s|m|h)$", + "type": "string" + }, + "nullInt64": { + "nullable": true, + "type": "integer" + }, + "nullTime": { + "format": "date-time", + "title": "NullTime implements sql.NullTime functionality.", + "type": "string" + }, + "oAuth2Client": { + "description": "OAuth 2.0 Clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "properties": { + "access_token_strategy": { + "description": "OAuth 2.0 Access Token Strategy\n\nAccessTokenStrategy is the strategy used to generate access tokens.\nValid options are `jwt` and `opaque`. `jwt` is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens\nSetting the stragegy here overrides the global setting in `strategies.access_token`.", + "type": "string" + }, + "allowed_cors_origins": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "authorization_code_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "authorization_code_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "authorization_code_grant_refresh_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "backchannel_logout_session_required": { + "description": "OpenID Connect Back-Channel Logout Session Required\n\nBoolean value specifying whether the RP requires that a sid (session ID) Claim be included in the Logout\nToken to identify the RP session with the OP when the backchannel_logout_uri is used.\nIf omitted, the default value is false.", + "type": "boolean" + }, + "backchannel_logout_uri": { + "description": "OpenID Connect Back-Channel Logout URI\n\nRP URL that will cause the RP to log itself out when sent a Logout Token by the OP.", + "type": "string" + }, + "client_credentials_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "client_id": { + "description": "OAuth 2.0 Client ID\n\nThe ID is autogenerated and immutable.", + "type": "string" + }, + "client_name": { + "description": "OAuth 2.0 Client Name\n\nThe human-readable name of the client to be presented to the\nend-user during authorization.", + "type": "string" + }, + "client_secret": { + "description": "OAuth 2.0 Client Secret\n\nThe secret will be included in the create request as cleartext, and then\nnever again. The secret is kept in hashed format and is not recoverable once lost.", + "type": "string" + }, + "client_secret_expires_at": { + "description": "OAuth 2.0 Client Secret Expires At\n\nThe field is currently not supported and its value is always 0.", + "format": "int64", + "type": "integer" + }, + "client_uri": { + "description": "OAuth 2.0 Client URI\n\nClientURI is a URL string of a web page providing information about the client.\nIf present, the server SHOULD display this URL to the end-user in\na clickable fashion.", + "type": "string" + }, + "contacts": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "created_at": { + "description": "OAuth 2.0 Client Creation Date\n\nCreatedAt returns the timestamp of the client's creation.", + "format": "date-time", + "type": "string" + }, + "frontchannel_logout_session_required": { + "description": "OpenID Connect Front-Channel Logout Session Required\n\nBoolean value specifying whether the RP requires that iss (issuer) and sid (session ID) query parameters be\nincluded to identify the RP session with the OP when the frontchannel_logout_uri is used.\nIf omitted, the default value is false.", + "type": "boolean" + }, + "frontchannel_logout_uri": { + "description": "OpenID Connect Front-Channel Logout URI\n\nRP URL that will cause the RP to log itself out when rendered in an iframe by the OP. An iss (issuer) query\nparameter and a sid (session ID) query parameter MAY be included by the OP to enable the RP to validate the\nrequest and to determine which of the potentially multiple sessions is to be logged out; if either is\nincluded, both MUST be.", + "type": "string" + }, + "grant_types": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "implicit_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "implicit_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "jwks": { + "description": "OAuth 2.0 Client JSON Web Key Set\n\nClient's JSON Web Key Set [JWK] document, passed by value. The semantics of the jwks parameter are the same as\nthe jwks_uri parameter, other than that the JWK Set is passed by value, rather than by reference. This parameter\nis intended only to be used by Clients that, for some reason, are unable to use the jwks_uri parameter, for\ninstance, by native applications that might not have a location to host the contents of the JWK Set. If a Client\ncan use jwks_uri, it MUST NOT use jwks. One significant downside of jwks is that it does not enable key rotation\n(which jwks_uri does, as described in Section 10 of OpenID Connect Core 1.0 [OpenID.Core]). The jwks_uri and jwks\nparameters MUST NOT be used together." + }, + "jwks_uri": { + "description": "OAuth 2.0 Client JSON Web Key Set URL\n\nURL for the Client's JSON Web Key Set [JWK] document. If the Client signs requests to the Server, it contains\nthe signing key(s) the Server uses to validate signatures from the Client. The JWK Set MAY also contain the\nClient's encryption keys(s), which are used by the Server to encrypt responses to the Client. When both signing\nand encryption keys are made available, a use (Key Use) parameter value is REQUIRED for all keys in the referenced\nJWK Set to indicate each key's intended usage. Although some algorithms allow the same key to be used for both\nsignatures and encryption, doing so is NOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used\nto provide X.509 representations of keys provided. When used, the bare key values MUST still be present and MUST\nmatch those in the certificate.", + "type": "string" + }, + "jwt_bearer_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "logo_uri": { + "description": "OAuth 2.0 Client Logo URI\n\nA URL string referencing the client's logo.", + "type": "string" + }, + "metadata": { + "$ref": "#/components/schemas/JSONRawMessage" + }, + "owner": { + "description": "OAuth 2.0 Client Owner\n\nOwner is a string identifying the owner of the OAuth 2.0 Client.", + "type": "string" + }, + "policy_uri": { + "description": "OAuth 2.0 Client Policy URI\n\nPolicyURI is a URL string that points to a human-readable privacy policy document\nthat describes how the deployment organization collects, uses,\nretains, and discloses personal data.", + "type": "string" + }, + "post_logout_redirect_uris": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "redirect_uris": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "refresh_token_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_refresh_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "registration_access_token": { + "description": "OpenID Connect Dynamic Client Registration Access Token\n\nRegistrationAccessToken can be used to update, get, or delete the OAuth2 Client. It is sent when creating a client\nusing Dynamic Client Registration.", + "type": "string" + }, + "registration_client_uri": { + "description": "OpenID Connect Dynamic Client Registration URL\n\nRegistrationClientURI is the URL used to update, get, or delete the OAuth2 Client.", + "type": "string" + }, + "request_object_signing_alg": { + "description": "OpenID Connect Request Object Signing Algorithm\n\nJWS [JWS] alg algorithm [JWA] that MUST be used for signing Request Objects sent to the OP. All Request Objects\nfrom this Client MUST be rejected, if not signed with this algorithm.", + "type": "string" + }, + "request_uris": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "response_types": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "scope": { + "description": "OAuth 2.0 Client Scope\n\nScope is a string containing a space-separated list of scope values (as\ndescribed in Section 3.3 of OAuth 2.0 [RFC6749]) that the client\ncan use when requesting access tokens.", + "example": "scope1 scope-2 scope.3 scope:4", + "type": "string" + }, + "sector_identifier_uri": { + "description": "OpenID Connect Sector Identifier URI\n\nURL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a\nfile with a single JSON array of redirect_uri values.", + "type": "string" + }, + "skip_consent": { + "description": "SkipConsent skips the consent screen for this client. This field can only\nbe set from the admin API.", + "type": "boolean" + }, + "subject_type": { + "description": "OpenID Connect Subject Type\n\nThe `subject_types_supported` Discovery parameter contains a\nlist of the supported subject_type values for this server. Valid types include `pairwise` and `public`.", + "type": "string" + }, + "token_endpoint_auth_method": { + "default": "client_secret_basic", + "description": "OAuth 2.0 Token Endpoint Authentication Method\n\nRequested Client Authentication method for the Token Endpoint. The options are:\n\n`client_secret_basic`: (default) Send `client_id` and `client_secret` as `application/x-www-form-urlencoded` encoded in the HTTP Authorization header.\n`client_secret_post`: Send `client_id` and `client_secret` as `application/x-www-form-urlencoded` in the HTTP body.\n`private_key_jwt`: Use JSON Web Tokens to authenticate the client.\n`none`: Used for public clients (native apps, mobile apps) which can not have secrets.", + "type": "string" + }, + "token_endpoint_auth_signing_alg": { + "description": "OAuth 2.0 Token Endpoint Signing Algorithm\n\nRequested Client Authentication signing algorithm for the Token Endpoint.", + "type": "string" + }, + "tos_uri": { + "description": "OAuth 2.0 Client Terms of Service URI\n\nA URL string pointing to a human-readable terms of service\ndocument for the client that describes a contractual relationship\nbetween the end-user and the client that the end-user accepts when\nauthorizing the client.", + "type": "string" + }, + "updated_at": { + "description": "OAuth 2.0 Client Last Update Date\n\nUpdatedAt returns the timestamp of the last update.", + "format": "date-time", + "type": "string" + }, + "userinfo_signed_response_alg": { + "description": "OpenID Connect Request Userinfo Signed Response Algorithm\n\nJWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT\n[JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims\nas a UTF-8 encoded JSON object using the application/json content-type.", + "type": "string" + } + }, + "title": "OAuth 2.0 Client", + "type": "object" + }, + "oAuth2ClientTokenLifespans": { + "description": "Lifespans of different token types issued for this OAuth 2.0 Client.", + "properties": { + "authorization_code_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "authorization_code_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "authorization_code_grant_refresh_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "client_credentials_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "implicit_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "implicit_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "jwt_bearer_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_refresh_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + } + }, + "title": "OAuth 2.0 Client Token Lifespans", + "type": "object" + }, + "oAuth2ConsentRequest": { + "properties": { + "acr": { + "description": "ACR represents the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it\nto express that, for example, a user authenticated using two factor authentication.", + "type": "string" + }, + "amr": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "challenge": { + "description": "ID is the identifier (\"authorization challenge\") of the consent authorization request. It is used to\nidentify the session.", + "type": "string" + }, + "client": { + "$ref": "#/components/schemas/oAuth2Client" + }, + "context": { + "$ref": "#/components/schemas/JSONRawMessage" + }, + "login_challenge": { + "description": "LoginChallenge is the login challenge this consent challenge belongs to. It can be used to associate\na login and consent request in the login & consent app.", + "type": "string" + }, + "login_session_id": { + "description": "LoginSessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag)\nthis ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false)\nthis will be a new random value. This value is used as the \"sid\" parameter in the ID Token and in OIDC Front-/Back-\nchannel logout. It's value can generally be used to associate consecutive login requests by a certain user.", + "type": "string" + }, + "oidc_context": { + "$ref": "#/components/schemas/oAuth2ConsentRequestOpenIDConnectContext" + }, + "request_url": { + "description": "RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which\ninitiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but\nmight come in handy if you want to deal with additional request parameters.", + "type": "string" + }, + "requested_access_token_audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "requested_scope": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "skip": { + "description": "Skip, if true, implies that the client has requested the same scopes from the same user previously.\nIf true, you must not ask the user to grant the requested scopes. You must however either allow or deny the\nconsent request using the usual API call.", + "type": "boolean" + }, + "subject": { + "description": "Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope\nrequested by the OAuth 2.0 client.", + "type": "string" + } + }, + "required": [ + "challenge" + ], + "title": "Contains information on an ongoing consent request.", + "type": "object" + }, + "oAuth2ConsentRequestOpenIDConnectContext": { + "properties": { + "acr_values": { + "description": "ACRValues is the Authentication AuthorizationContext Class Reference requested in the OAuth 2.0 Authorization request.\nIt is a parameter defined by OpenID Connect and expresses which level of authentication (e.g. 2FA) is required.\n\nOpenID Connect defines it as follows:\n> Requested Authentication AuthorizationContext Class Reference values. Space-separated string that specifies the acr values\nthat the Authorization Server is being requested to use for processing this Authentication Request, with the\nvalues appearing in order of preference. The Authentication AuthorizationContext Class satisfied by the authentication\nperformed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a\nVoluntary Claim by this parameter.", + "items": { + "type": "string" + }, + "type": "array" + }, + "display": { + "description": "Display is a string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User.\nThe defined values are:\npage: The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. If the display parameter is not specified, this is the default display mode.\npopup: The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over.\ntouch: The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface.\nwap: The Authorization Server SHOULD display the authentication and consent UI consistent with a \"feature phone\" type display.\n\nThe Authorization Server MAY also attempt to detect the capabilities of the User Agent and present an appropriate display.", + "type": "string" + }, + "id_token_hint_claims": { + "additionalProperties": {}, + "description": "IDTokenHintClaims are the claims of the ID Token previously issued by the Authorization Server being passed as a hint about the\nEnd-User's current or past authenticated session with the Client.", + "type": "object" + }, + "login_hint": { + "description": "LoginHint hints about the login identifier the End-User might use to log in (if necessary).\nThis hint can be used by an RP if it first asks the End-User for their e-mail address (or other identifier)\nand then wants to pass that value as a hint to the discovered authorization service. This value MAY also be a\nphone number in the format specified for the phone_number Claim. The use of this parameter is optional.", + "type": "string" + }, + "ui_locales": { + "description": "UILocales is the End-User'id preferred languages and scripts for the user interface, represented as a\nspace-separated list of BCP47 [RFC5646] language tag values, ordered by preference. For instance, the value\n\"fr-CA fr en\" represents a preference for French as spoken in Canada, then French (without a region designation),\nfollowed by English (without a region designation). An error SHOULD NOT result if some or all of the requested\nlocales are not supported by the OpenID Provider.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "title": "Contains optional information about the OpenID Connect request.", + "type": "object" + }, + "oAuth2ConsentSession": { + "description": "A completed OAuth 2.0 Consent Session.", + "properties": { + "consent_request": { + "$ref": "#/components/schemas/oAuth2ConsentRequest" + }, + "expires_at": { + "properties": { + "access_token": { + "format": "date-time", + "type": "string" + }, + "authorize_code": { + "format": "date-time", + "type": "string" + }, + "id_token": { + "format": "date-time", + "type": "string" + }, + "par_context": { + "format": "date-time", + "type": "string" + }, + "refresh_token": { + "format": "date-time", + "type": "string" + } + }, + "type": "object" + }, + "grant_access_token_audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "grant_scope": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "handled_at": { + "$ref": "#/components/schemas/nullTime" + }, + "remember": { + "description": "Remember Consent\n\nRemember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same\nclient asks the same user for the same, or a subset of, scope.", + "type": "boolean" + }, + "remember_for": { + "description": "Remember Consent For\n\nRememberFor sets how long the consent authorization should be remembered for in seconds. If set to `0`, the\nauthorization will be remembered indefinitely.", + "format": "int64", + "type": "integer" + }, + "session": { + "$ref": "#/components/schemas/acceptOAuth2ConsentRequestSession" + } + }, + "title": "OAuth 2.0 Consent Session", + "type": "object" + }, + "oAuth2ConsentSessions": { + "description": "List of OAuth 2.0 Consent Sessions", + "items": { + "$ref": "#/components/schemas/oAuth2ConsentSession" + }, + "type": "array" + }, + "oAuth2LoginRequest": { + "properties": { + "challenge": { + "description": "ID is the identifier (\"login challenge\") of the login request. It is used to\nidentify the session.", + "type": "string" + }, + "client": { + "$ref": "#/components/schemas/oAuth2Client" + }, + "oidc_context": { + "$ref": "#/components/schemas/oAuth2ConsentRequestOpenIDConnectContext" + }, + "request_url": { + "description": "RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which\ninitiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but\nmight come in handy if you want to deal with additional request parameters.", + "type": "string" + }, + "requested_access_token_audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "requested_scope": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "session_id": { + "description": "SessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag)\nthis ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false)\nthis will be a new random value. This value is used as the \"sid\" parameter in the ID Token and in OIDC Front-/Back-\nchannel logout. It's value can generally be used to associate consecutive login requests by a certain user.", + "type": "string" + }, + "skip": { + "description": "Skip, if true, implies that the client has requested the same scopes from the same user previously.\nIf true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL.\n\nThis feature allows you to update / set session information.", + "type": "boolean" + }, + "subject": { + "description": "Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope\nrequested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type\nwhen accepting the login request, or the request will fail.", + "type": "string" + } + }, + "required": [ + "challenge", + "requested_scope", + "requested_access_token_audience", + "skip", + "subject", + "client", + "request_url" + ], + "title": "Contains information on an ongoing login request.", + "type": "object" + }, + "oAuth2LogoutRequest": { + "properties": { + "challenge": { + "description": "Challenge is the identifier (\"logout challenge\") of the logout authentication request. It is used to\nidentify the session.", + "type": "string" + }, + "client": { + "$ref": "#/components/schemas/oAuth2Client" + }, + "request_url": { + "description": "RequestURL is the original Logout URL requested.", + "type": "string" + }, + "rp_initiated": { + "description": "RPInitiated is set to true if the request was initiated by a Relying Party (RP), also known as an OAuth 2.0 Client.", + "type": "boolean" + }, + "sid": { + "description": "SessionID is the login session ID that was requested to log out.", + "type": "string" + }, + "subject": { + "description": "Subject is the user for whom the logout was request.", + "type": "string" + } + }, + "title": "Contains information about an ongoing logout request.", + "type": "object" + }, + "oAuth2RedirectTo": { + "description": "Contains a redirect URL used to complete a login, consent, or logout request.", + "properties": { + "redirect_to": { + "description": "RedirectURL is the URL which you should redirect the user's browser to once the authentication process is completed.", + "type": "string" + } + }, + "required": [ + "redirect_to" + ], + "title": "OAuth 2.0 Redirect Browser To", + "type": "object" + }, + "oAuth2TokenExchange": { + "description": "OAuth2 Token Exchange Result", + "properties": { + "access_token": { + "description": "The access token issued by the authorization server.", + "type": "string" + }, + "expires_in": { + "description": "The lifetime in seconds of the access token. For\nexample, the value \"3600\" denotes that the access token will\nexpire in one hour from the time the response was generated.", + "format": "int64", + "type": "integer" + }, + "id_token": { + "description": "To retrieve a refresh token request the id_token scope.", + "format": "int64", + "type": "integer" + }, + "refresh_token": { + "description": "The refresh token, which can be used to obtain new\naccess tokens. To retrieve it add the scope \"offline\" to your access token request.", + "type": "string" + }, + "scope": { + "description": "The scope of the access token", + "type": "string" + }, + "token_type": { + "description": "The type of the token issued", + "type": "string" + } + }, + "type": "object" + }, + "oidcConfiguration": { + "description": "Includes links to several endpoints (for example `/oauth2/token`) and exposes information on supported signature algorithms\namong others.", + "properties": { + "authorization_endpoint": { + "description": "OAuth 2.0 Authorization Endpoint URL", + "example": "https://playground.ory.sh/ory-hydra/public/oauth2/auth", + "type": "string" + }, + "backchannel_logout_session_supported": { + "description": "OpenID Connect Back-Channel Logout Session Required\n\nBoolean value specifying whether the OP can pass a sid (session ID) Claim in the Logout Token to identify the RP\nsession with the OP. If supported, the sid Claim is also included in ID Tokens issued by the OP", + "type": "boolean" + }, + "backchannel_logout_supported": { + "description": "OpenID Connect Back-Channel Logout Supported\n\nBoolean value specifying whether the OP supports back-channel logout, with true indicating support.", + "type": "boolean" + }, + "claims_parameter_supported": { + "description": "OpenID Connect Claims Parameter Parameter Supported\n\nBoolean value specifying whether the OP supports use of the claims parameter, with true indicating support.", + "type": "boolean" + }, + "claims_supported": { + "description": "OpenID Connect Supported Claims\n\nJSON array containing a list of the Claim Names of the Claims that the OpenID Provider MAY be able to supply\nvalues for. Note that for privacy or other reasons, this might not be an exhaustive list.", + "items": { + "type": "string" + }, + "type": "array" + }, + "code_challenge_methods_supported": { + "description": "OAuth 2.0 PKCE Supported Code Challenge Methods\n\nJSON array containing a list of Proof Key for Code Exchange (PKCE) [RFC7636] code challenge methods supported\nby this authorization server.", + "items": { + "type": "string" + }, + "type": "array" + }, + "end_session_endpoint": { + "description": "OpenID Connect End-Session Endpoint\n\nURL at the OP to which an RP can perform a redirect to request that the End-User be logged out at the OP.", + "type": "string" + }, + "frontchannel_logout_session_supported": { + "description": "OpenID Connect Front-Channel Logout Session Required\n\nBoolean value specifying whether the OP can pass iss (issuer) and sid (session ID) query parameters to identify\nthe RP session with the OP when the frontchannel_logout_uri is used. If supported, the sid Claim is also\nincluded in ID Tokens issued by the OP.", + "type": "boolean" + }, + "frontchannel_logout_supported": { + "description": "OpenID Connect Front-Channel Logout Supported\n\nBoolean value specifying whether the OP supports HTTP-based logout, with true indicating support.", + "type": "boolean" + }, + "grant_types_supported": { + "description": "OAuth 2.0 Supported Grant Types\n\nJSON array containing a list of the OAuth 2.0 Grant Type values that this OP supports.", + "items": { + "type": "string" + }, + "type": "array" + }, + "id_token_signed_response_alg": { + "description": "OpenID Connect Default ID Token Signing Algorithms\n\nAlgorithm used to sign OpenID Connect ID Tokens.", + "items": { + "type": "string" + }, + "type": "array" + }, + "id_token_signing_alg_values_supported": { + "description": "OpenID Connect Supported ID Token Signing Algorithms\n\nJSON array containing a list of the JWS signing algorithms (alg values) supported by the OP for the ID Token\nto encode the Claims in a JWT.", + "items": { + "type": "string" + }, + "type": "array" + }, + "issuer": { + "description": "OpenID Connect Issuer URL\n\nAn URL using the https scheme with no query or fragment component that the OP asserts as its IssuerURL Identifier.\nIf IssuerURL discovery is supported , this value MUST be identical to the issuer value returned\nby WebFinger. This also MUST be identical to the iss Claim value in ID Tokens issued from this IssuerURL.", + "example": "https://playground.ory.sh/ory-hydra/public/", + "type": "string" + }, + "jwks_uri": { + "description": "OpenID Connect Well-Known JSON Web Keys URL\n\nURL of the OP's JSON Web Key Set [JWK] document. This contains the signing key(s) the RP uses to validate\nsignatures from the OP. The JWK Set MAY also contain the Server's encryption key(s), which are used by RPs\nto encrypt requests to the Server. When both signing and encryption keys are made available, a use (Key Use)\nparameter value is REQUIRED for all keys in the referenced JWK Set to indicate each key's intended usage.\nAlthough some algorithms allow the same key to be used for both signatures and encryption, doing so is\nNOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of\nkeys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.", + "example": "https://{slug}.projects.oryapis.com/.well-known/jwks.json", + "type": "string" + }, + "registration_endpoint": { + "description": "OpenID Connect Dynamic Client Registration Endpoint URL", + "example": "https://playground.ory.sh/ory-hydra/admin/client", + "type": "string" + }, + "request_object_signing_alg_values_supported": { + "description": "OpenID Connect Supported Request Object Signing Algorithms\n\nJSON array containing a list of the JWS signing algorithms (alg values) supported by the OP for Request Objects,\nwhich are described in Section 6.1 of OpenID Connect Core 1.0 [OpenID.Core]. These algorithms are used both when\nthe Request Object is passed by value (using the request parameter) and when it is passed by reference\n(using the request_uri parameter).", + "items": { + "type": "string" + }, + "type": "array" + }, + "request_parameter_supported": { + "description": "OpenID Connect Request Parameter Supported\n\nBoolean value specifying whether the OP supports use of the request parameter, with true indicating support.", + "type": "boolean" + }, + "request_uri_parameter_supported": { + "description": "OpenID Connect Request URI Parameter Supported\n\nBoolean value specifying whether the OP supports use of the request_uri parameter, with true indicating support.", + "type": "boolean" + }, + "require_request_uri_registration": { + "description": "OpenID Connect Requires Request URI Registration\n\nBoolean value specifying whether the OP requires any request_uri values used to be pre-registered\nusing the request_uris registration parameter.", + "type": "boolean" + }, + "response_modes_supported": { + "description": "OAuth 2.0 Supported Response Modes\n\nJSON array containing a list of the OAuth 2.0 response_mode values that this OP supports.", + "items": { + "type": "string" + }, + "type": "array" + }, + "response_types_supported": { + "description": "OAuth 2.0 Supported Response Types\n\nJSON array containing a list of the OAuth 2.0 response_type values that this OP supports. Dynamic OpenID\nProviders MUST support the code, id_token, and the token id_token Response Type values.", + "items": { + "type": "string" + }, + "type": "array" + }, + "revocation_endpoint": { + "description": "OAuth 2.0 Token Revocation URL\n\nURL of the authorization server's OAuth 2.0 revocation endpoint.", + "type": "string" + }, + "scopes_supported": { + "description": "OAuth 2.0 Supported Scope Values\n\nJSON array containing a list of the OAuth 2.0 [RFC6749] scope values that this server supports. The server MUST\nsupport the openid scope value. Servers MAY choose not to advertise some supported scope values even when this parameter is used", + "items": { + "type": "string" + }, + "type": "array" + }, + "subject_types_supported": { + "description": "OpenID Connect Supported Subject Types\n\nJSON array containing a list of the Subject Identifier types that this OP supports. Valid types include\npairwise and public.", + "items": { + "type": "string" + }, + "type": "array" + }, + "token_endpoint": { + "description": "OAuth 2.0 Token Endpoint URL", + "example": "https://playground.ory.sh/ory-hydra/public/oauth2/token", + "type": "string" + }, + "token_endpoint_auth_methods_supported": { + "description": "OAuth 2.0 Supported Client Authentication Methods\n\nJSON array containing a list of Client Authentication methods supported by this Token Endpoint. The options are\nclient_secret_post, client_secret_basic, client_secret_jwt, and private_key_jwt, as described in Section 9 of OpenID Connect Core 1.0", + "items": { + "type": "string" + }, + "type": "array" + }, + "userinfo_endpoint": { + "description": "OpenID Connect Userinfo URL\n\nURL of the OP's UserInfo Endpoint.", + "type": "string" + }, + "userinfo_signed_response_alg": { + "description": "OpenID Connect User Userinfo Signing Algorithm\n\nAlgorithm used to sign OpenID Connect Userinfo Responses.", + "items": { + "type": "string" + }, + "type": "array" + }, + "userinfo_signing_alg_values_supported": { + "description": "OpenID Connect Supported Userinfo Signing Algorithm\n\nJSON array containing a list of the JWS [JWS] signing algorithms (alg values) [JWA] supported by the UserInfo Endpoint to encode the Claims in a JWT [JWT].", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "issuer", + "authorization_endpoint", + "token_endpoint", + "jwks_uri", + "subject_types_supported", + "response_types_supported", + "id_token_signing_alg_values_supported", + "id_token_signed_response_alg", + "userinfo_signed_response_alg" + ], + "title": "OpenID Connect Discovery Metadata", + "type": "object" + }, + "oidcUserInfo": { + "description": "OpenID Connect Userinfo", + "properties": { + "birthdate": { + "description": "End-User's birthday, represented as an ISO 8601:2004 [ISO8601‑2004] YYYY-MM-DD format. The year MAY be 0000, indicating that it is omitted. To represent only the year, YYYY format is allowed. Note that depending on the underlying platform's date related function, providing just year can result in varying month and day, so the implementers need to take this factor into account to correctly process the dates.", + "type": "string" + }, + "email": { + "description": "End-User's preferred e-mail address. Its value MUST conform to the RFC 5322 [RFC5322] addr-spec syntax. The RP MUST NOT rely upon this value being unique, as discussed in Section 5.7.", + "type": "string" + }, + "email_verified": { + "description": "True if the End-User's e-mail address has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this e-mail address was controlled by the End-User at the time the verification was performed. The means by which an e-mail address is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating.", + "type": "boolean" + }, + "family_name": { + "description": "Surname(s) or last name(s) of the End-User. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters.", + "type": "string" + }, + "gender": { + "description": "End-User's gender. Values defined by this specification are female and male. Other values MAY be used when neither of the defined values are applicable.", + "type": "string" + }, + "given_name": { + "description": "Given name(s) or first name(s) of the End-User. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters.", + "type": "string" + }, + "locale": { + "description": "End-User's locale, represented as a BCP47 [RFC5646] language tag. This is typically an ISO 639-1 Alpha-2 [ISO639‑1] language code in lowercase and an ISO 3166-1 Alpha-2 [ISO3166‑1] country code in uppercase, separated by a dash. For example, en-US or fr-CA. As a compatibility note, some implementations have used an underscore as the separator rather than a dash, for example, en_US; Relying Parties MAY choose to accept this locale syntax as well.", + "type": "string" + }, + "middle_name": { + "description": "Middle name(s) of the End-User. Note that in some cultures, people can have multiple middle names; all can be present, with the names being separated by space characters. Also note that in some cultures, middle names are not used.", + "type": "string" + }, + "name": { + "description": "End-User's full name in displayable form including all name parts, possibly including titles and suffixes, ordered according to the End-User's locale and preferences.", + "type": "string" + }, + "nickname": { + "description": "Casual name of the End-User that may or may not be the same as the given_name. For instance, a nickname value of Mike might be returned alongside a given_name value of Michael.", + "type": "string" + }, + "phone_number": { + "description": "End-User's preferred telephone number. E.164 [E.164] is RECOMMENDED as the format of this Claim, for example, +1 (425) 555-1212 or +56 (2) 687 2400. If the phone number contains an extension, it is RECOMMENDED that the extension be represented using the RFC 3966 [RFC3966] extension syntax, for example, +1 (604) 555-1234;ext=5678.", + "type": "string" + }, + "phone_number_verified": { + "description": "True if the End-User's phone number has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this phone number was controlled by the End-User at the time the verification was performed. The means by which a phone number is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating. When true, the phone_number Claim MUST be in E.164 format and any extensions MUST be represented in RFC 3966 format.", + "type": "boolean" + }, + "picture": { + "description": "URL of the End-User's profile picture. This URL MUST refer to an image file (for example, a PNG, JPEG, or GIF image file), rather than to a Web page containing an image. Note that this URL SHOULD specifically reference a profile photo of the End-User suitable for displaying when describing the End-User, rather than an arbitrary photo taken by the End-User.", + "type": "string" + }, + "preferred_username": { + "description": "Non-unique shorthand name by which the End-User wishes to be referred to at the RP, such as janedoe or j.doe. This value MAY be any valid JSON string including special characters such as @, /, or whitespace.", + "type": "string" + }, + "profile": { + "description": "URL of the End-User's profile page. The contents of this Web page SHOULD be about the End-User.", + "type": "string" + }, + "sub": { + "description": "Subject - Identifier for the End-User at the IssuerURL.", + "type": "string" + }, + "updated_at": { + "description": "Time the End-User's information was last updated. Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time.", + "format": "int64", + "type": "integer" + }, + "website": { + "description": "URL of the End-User's Web page or blog. This Web page SHOULD contain information published by the End-User or an organization that the End-User is affiliated with.", + "type": "string" + }, + "zoneinfo": { + "description": "String from zoneinfo [zoneinfo] time zone database representing the End-User's time zone. For example, Europe/Paris or America/Los_Angeles.", + "type": "string" + } + }, + "type": "object" + }, + "pagination": { + "properties": { + "page_size": { + "default": 250, + "description": "Items per page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "format": "int64", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "page_token": { + "default": "1", + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "minimum": 1, + "type": "string" + } + }, + "type": "object" + }, + "paginationHeaders": { + "properties": { + "link": { + "description": "The link header contains pagination links.\n\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\nin: header", + "type": "string" + }, + "x-total-count": { + "description": "The total number of clients.\n\nin: header", + "type": "string" + } + }, + "type": "object" + }, + "rejectOAuth2Request": { + "properties": { + "error": { + "description": "The error should follow the OAuth2 error format (e.g. `invalid_request`, `login_required`).\n\nDefaults to `request_denied`.", + "type": "string" + }, + "error_debug": { + "description": "Debug contains information to help resolve the problem as a developer. Usually not exposed\nto the public but only in the server logs.", + "type": "string" + }, + "error_description": { + "description": "Description of the error in a human readable format.", + "type": "string" + }, + "error_hint": { + "description": "Hint to help resolve the error.", + "type": "string" + }, + "status_code": { + "description": "Represents the HTTP status code of the error (e.g. 401 or 403)\n\nDefaults to 400", + "format": "int64", + "type": "integer" + } + }, + "title": "The request payload used to accept a login or consent request.", + "type": "object" + }, + "tokenPagination": { + "properties": { + "page_size": { + "default": 250, + "description": "Items per page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "format": "int64", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "page_token": { + "default": "1", + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "minimum": 1, + "type": "string" + } + }, + "type": "object" + }, + "tokenPaginationHeaders": { + "properties": { + "link": { + "description": "The link header contains pagination links.\n\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\nin: header", + "type": "string" + }, + "x-total-count": { + "description": "The total number of clients.\n\nin: header", + "type": "string" + } + }, + "type": "object" + }, + "tokenPaginationRequestParameters": { + "description": "The `Link` HTTP header contains multiple links (`first`, `next`, `last`, `previous`) formatted as:\n`; rel=\"{page}\"`\n\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "properties": { + "page_size": { + "default": 250, + "description": "Items per Page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "format": "int64", + "maximum": 500, + "minimum": 1, + "type": "integer" + }, + "page_token": { + "default": "1", + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "minimum": 1, + "type": "string" + } + }, + "title": "Pagination Request Parameters", + "type": "object" + }, + "tokenPaginationResponseHeaders": { + "description": "The `Link` HTTP header contains multiple links (`first`, `next`, `last`, `previous`) formatted as:\n`; rel=\"{page}\"`\n\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "properties": { + "link": { + "description": "The Link HTTP Header\n\nThe `Link` header contains a comma-delimited list of links to the following pages:\n\nfirst: The first page of results.\nnext: The next page of results.\nprev: The previous page of results.\nlast: The last page of results.\n\nPages are omitted if they do not exist. For example, if there is no next page, the `next` link is omitted. Examples:\n\n; rel=\"first\",; rel=\"next\",; rel=\"prev\",; rel=\"last\"", + "type": "string" + }, + "x-total-count": { + "description": "The X-Total-Count HTTP Header\n\nThe `X-Total-Count` header contains the total number of items in the collection.", + "format": "int64", + "type": "integer" + } + }, + "title": "Pagination Response Header", + "type": "object" + }, + "trustOAuth2JwtGrantIssuer": { + "description": "Trust OAuth2 JWT Bearer Grant Type Issuer Request Body", + "properties": { + "allow_any_subject": { + "description": "The \"allow_any_subject\" indicates that the issuer is allowed to have any principal as the subject of the JWT.", + "type": "boolean" + }, + "expires_at": { + "description": "The \"expires_at\" indicates, when grant will expire, so we will reject assertion from \"issuer\" targeting \"subject\".", + "format": "date-time", + "type": "string" + }, + "issuer": { + "description": "The \"issuer\" identifies the principal that issued the JWT assertion (same as \"iss\" claim in JWT).", + "example": "https://jwt-idp.example.com", + "type": "string" + }, + "jwk": { + "$ref": "#/components/schemas/jsonWebKey" + }, + "scope": { + "description": "The \"scope\" contains list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749])", + "example": [ + "openid", + "offline" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "subject": { + "description": "The \"subject\" identifies the principal that is the subject of the JWT.", + "example": "mike@example.com", + "type": "string" + } + }, + "required": [ + "issuer", + "scope", + "jwk", + "expires_at" + ], + "type": "object" + }, + "trustedOAuth2JwtGrantIssuer": { + "description": "OAuth2 JWT Bearer Grant Type Issuer Trust Relationship", + "properties": { + "allow_any_subject": { + "description": "The \"allow_any_subject\" indicates that the issuer is allowed to have any principal as the subject of the JWT.", + "type": "boolean" + }, + "created_at": { + "description": "The \"created_at\" indicates, when grant was created.", + "format": "date-time", + "type": "string" + }, + "expires_at": { + "description": "The \"expires_at\" indicates, when grant will expire, so we will reject assertion from \"issuer\" targeting \"subject\".", + "format": "date-time", + "type": "string" + }, + "id": { + "example": "9edc811f-4e28-453c-9b46-4de65f00217f", + "type": "string" + }, + "issuer": { + "description": "The \"issuer\" identifies the principal that issued the JWT assertion (same as \"iss\" claim in JWT).", + "example": "https://jwt-idp.example.com", + "type": "string" + }, + "public_key": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantJsonWebKey" + }, + "scope": { + "description": "The \"scope\" contains list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749])", + "example": [ + "openid", + "offline" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "subject": { + "description": "The \"subject\" identifies the principal that is the subject of the JWT.", + "example": "mike@example.com", + "type": "string" + } + }, + "type": "object" + }, + "trustedOAuth2JwtGrantIssuers": { + "description": "OAuth2 JWT Bearer Grant Type Issuer Trust Relationships", + "items": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantIssuer" + }, + "type": "array" + }, + "trustedOAuth2JwtGrantJsonWebKey": { + "description": "OAuth2 JWT Bearer Grant Type Issuer Trusted JSON Web Key", + "properties": { + "kid": { + "description": "The \"key_id\" is key unique identifier (same as kid header in jws/jwt).", + "example": "123e4567-e89b-12d3-a456-426655440000", + "type": "string" + }, + "set": { + "description": "The \"set\" is basically a name for a group(set) of keys. Will be the same as \"issuer\" in grant.", + "example": "https://jwt-idp.example.com", + "type": "string" + } + }, + "type": "object" + }, + "unexpectedError": { + "type": "string" + }, + "version": { + "properties": { + "version": { + "description": "Version is the service's version.", + "type": "string" + } + }, + "type": "object" + } + }, + "securitySchemes": { + "basic": { + "scheme": "basic", + "type": "http" + }, + "bearer": { + "scheme": "bearer", + "type": "http" + }, + "oauth2": { + "flows": { + "authorizationCode": { + "authorizationUrl": "https://hydra.demo.ory.sh/oauth2/auth", + "scopes": { + "offline": "A scope required when requesting refresh tokens (alias for `offline_access`)", + "offline_access": "A scope required when requesting refresh tokens", + "openid": "Request an OpenID Connect ID Token" + }, + "tokenUrl": "https://hydra.demo.ory.sh/oauth2/token" + } + }, + "type": "oauth2" + } + } + }, + "info": { + "contact": { + "email": "hi@ory.sh" + }, + "description": "Documentation for all of Ory Hydra's APIs.\n", + "license": { + "name": "Apache 2.0" + }, + "title": "Ory Hydra API", + "version": "" + }, + "openapi": "3.0.3", + "paths": { + "/.well-known/jwks.json": { + "get": { + "description": "This endpoint returns JSON Web Keys required to verifying OpenID Connect ID Tokens and,\nif enabled, OAuth 2.0 JWT Access Tokens. This endpoint can be used with client libraries like\n[node-jwks-rsa](https://github.com/auth0/node-jwks-rsa) among others.", + "operationId": "discoverJsonWebKeys", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Discover Well-Known JSON Web Keys", + "tags": [ + "wellknown" + ] + } + }, + "/.well-known/openid-configuration": { + "get": { + "description": "A mechanism for an OpenID Connect Relying Party to discover the End-User's OpenID Provider and obtain information needed to interact with it, including its OAuth 2.0 endpoint locations.\n\nPopular libraries for OpenID Connect clients include oidc-client-js (JavaScript), go-oidc (Golang), and others.\nFor a full list of clients go here: https://openid.net/developers/certified/", + "operationId": "discoverOidcConfiguration", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oidcConfiguration" + } + } + }, + "description": "oidcConfiguration" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "OpenID Connect Discovery", + "tags": [ + "oidc" + ] + } + }, + "/admin/clients": { + "get": { + "description": "This endpoint lists all clients in the database, and never returns client secrets.\nAs a default it lists the first 100 clients.", + "operationId": "listOAuth2Clients", + "parameters": [ + { + "description": "Items per Page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "in": "query", + "name": "page_size", + "schema": { + "default": 250, + "format": "int64", + "maximum": 500, + "minimum": 1, + "type": "integer" + } + }, + { + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "in": "query", + "name": "page_token", + "schema": { + "default": "1", + "minimum": 1, + "type": "string" + } + }, + { + "description": "The name of the clients to filter by.", + "in": "query", + "name": "client_name", + "schema": { + "type": "string" + } + }, + { + "description": "The owner of the clients to filter by.", + "in": "query", + "name": "owner", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/listOAuth2Clients" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "List OAuth 2.0 Clients", + "tags": [ + "oAuth2" + ] + }, + "post": { + "description": "Create a new OAuth 2.0 client. If you pass `client_secret` the secret is used, otherwise a random secret\nis generated. The secret is echoed in the response. It is not possible to retrieve it later on.", + "operationId": "createOAuth2Client", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "OAuth 2.0 Client Request Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "400": { + "$ref": "#/components/responses/errorOAuth2BadRequest" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Create OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/clients/{id}": { + "delete": { + "description": "Delete an existing OAuth 2.0 Client by its ID.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.\n\nMake sure that this endpoint is well protected and only callable by first-party components.", + "operationId": "deleteOAuth2Client", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Delete OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + }, + "get": { + "description": "Get an OAuth 2.0 client by its ID. This endpoint never returns the client secret.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "getOAuth2Client", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Get an OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + }, + "patch": { + "description": "Patch an existing OAuth 2.0 Client using JSON Patch. If you pass `client_secret`\nthe secret will be updated and returned via the API. This is the\nonly time you will be able to retrieve the client secret, so write it down and keep it safe.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "patchOAuth2Client", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonPatchDocument" + } + } + }, + "description": "OAuth 2.0 Client JSON Patch Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "404": { + "$ref": "#/components/responses/errorOAuth2NotFound" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Patch OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + }, + "put": { + "description": "Replaces an existing OAuth 2.0 Client with the payload you send. If you pass `client_secret` the secret is used,\notherwise the existing secret is used.\n\nIf set, the secret is echoed in the response. It is not possible to retrieve it later on.\n\nOAuth 2.0 Clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "setOAuth2Client", + "parameters": [ + { + "description": "OAuth 2.0 Client ID", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "OAuth 2.0 Client Request Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "400": { + "$ref": "#/components/responses/errorOAuth2BadRequest" + }, + "404": { + "$ref": "#/components/responses/errorOAuth2NotFound" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Set OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/clients/{id}/lifespans": { + "put": { + "description": "Set lifespans of different token types issued for this OAuth 2.0 client. Does not modify other fields.", + "operationId": "setOAuth2ClientLifespans", + "parameters": [ + { + "description": "OAuth 2.0 Client ID", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2ClientTokenLifespans" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Set OAuth2 Client Token Lifespans", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/keys/{set}": { + "delete": { + "description": "Use this endpoint to delete a complete JSON Web Key Set and all the keys in that set.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "deleteJsonWebKeySet", + "parameters": [ + { + "description": "The JSON Web Key Set", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Delete JSON Web Key Set", + "tags": [ + "jwk" + ] + }, + "get": { + "description": "This endpoint can be used to retrieve JWK Sets stored in ORY Hydra.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "getJsonWebKeySet", + "parameters": [ + { + "description": "JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Retrieve a JSON Web Key Set", + "tags": [ + "jwk" + ] + }, + "post": { + "description": "This endpoint is capable of generating JSON Web Key Sets for you. There a different strategies available, such as symmetric cryptographic keys (HS256, HS512) and asymetric cryptographic keys (RS256, ECDSA). If the specified JSON Web Key Set does not exist, it will be created.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "createJsonWebKeySet", + "parameters": [ + { + "description": "The JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createJsonWebKeySet" + } + } + }, + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Create JSON Web Key", + "tags": [ + "jwk" + ] + }, + "put": { + "description": "Use this method if you do not want to let Hydra generate the JWKs for you, but instead save your own.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "setJsonWebKeySet", + "parameters": [ + { + "description": "The JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Update a JSON Web Key Set", + "tags": [ + "jwk" + ] + } + }, + "/admin/keys/{set}/{kid}": { + "delete": { + "description": "Use this endpoint to delete a single JSON Web Key.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A\nJWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses\nthis functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens),\nand allows storing user-defined keys as well.", + "operationId": "deleteJsonWebKey", + "parameters": [ + { + "description": "The JSON Web Key Set", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The JSON Web Key ID (kid)", + "in": "path", + "name": "kid", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Delete JSON Web Key", + "tags": [ + "jwk" + ] + }, + "get": { + "description": "This endpoint returns a singular JSON Web Key contained in a set. It is identified by the set and the specific key ID (kid).", + "operationId": "getJsonWebKey", + "parameters": [ + { + "description": "JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "JSON Web Key ID", + "in": "path", + "name": "kid", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Get JSON Web Key", + "tags": [ + "jwk" + ] + }, + "put": { + "description": "Use this method if you do not want to let Hydra generate the JWKs for you, but instead save your own.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "setJsonWebKey", + "parameters": [ + { + "description": "The JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "JSON Web Key ID", + "in": "path", + "name": "kid", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKey" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKey" + } + } + }, + "description": "jsonWebKey" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Set JSON Web Key", + "tags": [ + "jwk" + ] + } + }, + "/admin/oauth2/auth/requests/consent": { + "get": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell Ory now about it. If the subject authenticated, he/she must now be asked if\nthe OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the subject's behalf.\n\nThe consent challenge is appended to the consent provider's URL to which the subject's user-agent (browser) is redirected to. The consent\nprovider uses that challenge to fetch information on the OAuth2 request and then tells Ory if the subject accepted\nor rejected the request.\n\nThe default consent provider is available via the Ory Managed Account Experience. To customize the consent provider, please\nhead over to the OAuth 2.0 documentation.", + "operationId": "getOAuth2ConsentRequest", + "parameters": [ + { + "description": "OAuth 2.0 Consent Request Challenge", + "in": "query", + "name": "consent_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2ConsentRequest" + } + } + }, + "description": "oAuth2ConsentRequest" + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Get OAuth 2.0 Consent Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/consent/accept": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell Ory now about it. If the subject authenticated, he/she must now be asked if\nthe OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the subject's behalf.\n\nThe consent challenge is appended to the consent provider's URL to which the subject's user-agent (browser) is redirected to. The consent\nprovider uses that challenge to fetch information on the OAuth2 request and then tells Ory if the subject accepted\nor rejected the request.\n\nThis endpoint tells Ory that the subject has authorized the OAuth 2.0 client to access resources on his/her behalf.\nThe consent provider includes additional information, such as session data for access and ID tokens, and if the\nconsent request should be used as basis for future requests.\n\nThe response contains a redirect URL which the consent provider should redirect the user-agent to.\n\nThe default consent provider is available via the Ory Managed Account Experience. To customize the consent provider, please\nhead over to the OAuth 2.0 documentation.", + "operationId": "acceptOAuth2ConsentRequest", + "parameters": [ + { + "description": "OAuth 2.0 Consent Request Challenge", + "in": "query", + "name": "consent_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/acceptOAuth2ConsentRequest" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Accept OAuth 2.0 Consent Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/consent/reject": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell Ory now about it. If the subject authenticated, he/she must now be asked if\nthe OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the subject's behalf.\n\nThe consent challenge is appended to the consent provider's URL to which the subject's user-agent (browser) is redirected to. The consent\nprovider uses that challenge to fetch information on the OAuth2 request and then tells Ory if the subject accepted\nor rejected the request.\n\nThis endpoint tells Ory that the subject has not authorized the OAuth 2.0 client to access resources on his/her behalf.\nThe consent provider must include a reason why the consent was not granted.\n\nThe response contains a redirect URL which the consent provider should redirect the user-agent to.\n\nThe default consent provider is available via the Ory Managed Account Experience. To customize the consent provider, please\nhead over to the OAuth 2.0 documentation.", + "operationId": "rejectOAuth2ConsentRequest", + "parameters": [ + { + "description": "OAuth 2.0 Consent Request Challenge", + "in": "query", + "name": "consent_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rejectOAuth2Request" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Reject OAuth 2.0 Consent Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/login": { + "get": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell the Ory OAuth2 Service about it.\n\nPer default, the login provider is Ory itself. You may use a different login provider which needs to be a web-app\nyou write and host, and it must be able to authenticate (\"show the subject a login screen\")\na subject (in OAuth2 the proper name for subject is \"resource owner\").\n\nThe authentication challenge is appended to the login provider URL to which the subject's user-agent (browser) is redirected to. The login\nprovider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process.", + "operationId": "getOAuth2LoginRequest", + "parameters": [ + { + "description": "OAuth 2.0 Login Request Challenge", + "in": "query", + "name": "login_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2LoginRequest" + } + } + }, + "description": "oAuth2LoginRequest" + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Get OAuth 2.0 Login Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/login/accept": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell the Ory OAuth2 Service about it.\n\nThe authentication challenge is appended to the login provider URL to which the subject's user-agent (browser) is redirected to. The login\nprovider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process.\n\nThis endpoint tells Ory that the subject has successfully authenticated and includes additional information such as\nthe subject's ID and if Ory should remember the subject's subject agent for future authentication attempts by setting\na cookie.\n\nThe response contains a redirect URL which the login provider should redirect the user-agent to.", + "operationId": "acceptOAuth2LoginRequest", + "parameters": [ + { + "description": "OAuth 2.0 Login Request Challenge", + "in": "query", + "name": "login_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/acceptOAuth2LoginRequest" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Accept OAuth 2.0 Login Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/login/reject": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell the Ory OAuth2 Service about it.\n\nThe authentication challenge is appended to the login provider URL to which the subject's user-agent (browser) is redirected to. The login\nprovider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process.\n\nThis endpoint tells Ory that the subject has not authenticated and includes a reason why the authentication\nwas denied.\n\nThe response contains a redirect URL which the login provider should redirect the user-agent to.", + "operationId": "rejectOAuth2LoginRequest", + "parameters": [ + { + "description": "OAuth 2.0 Login Request Challenge", + "in": "query", + "name": "login_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rejectOAuth2Request" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Reject OAuth 2.0 Login Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/logout": { + "get": { + "description": "Use this endpoint to fetch an Ory OAuth 2.0 logout request.", + "operationId": "getOAuth2LogoutRequest", + "parameters": [ + { + "in": "query", + "name": "logout_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2LogoutRequest" + } + } + }, + "description": "oAuth2LogoutRequest" + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Get OAuth 2.0 Session Logout Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/logout/accept": { + "put": { + "description": "When a user or an application requests Ory OAuth 2.0 to remove the session state of a subject, this endpoint is used to confirm that logout request.\n\nThe response contains a redirect URL which the consent provider should redirect the user-agent to.", + "operationId": "acceptOAuth2LogoutRequest", + "parameters": [ + { + "description": "OAuth 2.0 Logout Request Challenge", + "in": "query", + "name": "logout_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Accept OAuth 2.0 Session Logout Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/logout/reject": { + "put": { + "description": "When a user or an application requests Ory OAuth 2.0 to remove the session state of a subject, this endpoint is used to deny that logout request.\nNo HTTP request body is required.\n\nThe response is empty as the logout provider has to chose what action to perform next.", + "operationId": "rejectOAuth2LogoutRequest", + "parameters": [ + { + "in": "query", + "name": "logout_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Reject OAuth 2.0 Session Logout Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/sessions/consent": { + "delete": { + "description": "This endpoint revokes a subject's granted consent sessions and invalidates all\nassociated OAuth 2.0 Access Tokens. You may also only revoke sessions for a specific OAuth 2.0 Client ID.", + "operationId": "revokeOAuth2ConsentSessions", + "parameters": [ + { + "description": "OAuth 2.0 Consent Subject\n\nThe subject whose consent sessions should be deleted.", + "in": "query", + "name": "subject", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "OAuth 2.0 Client ID\n\nIf set, deletes only those consent sessions that have been granted to the specified OAuth 2.0 Client ID.", + "in": "query", + "name": "client", + "schema": { + "type": "string" + } + }, + { + "description": "Revoke All Consent Sessions\n\nIf set to `true` deletes all consent sessions by the Subject that have been granted.", + "in": "query", + "name": "all", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Revoke OAuth 2.0 Consent Sessions of a Subject", + "tags": [ + "oAuth2" + ] + }, + "get": { + "description": "This endpoint lists all subject's granted consent sessions, including client and granted scope.\nIf the subject is unknown or has not granted any consent sessions yet, the endpoint returns an\nempty JSON array with status code 200 OK.", + "operationId": "listOAuth2ConsentSessions", + "parameters": [ + { + "description": "Items per Page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "in": "query", + "name": "page_size", + "schema": { + "default": 250, + "format": "int64", + "maximum": 500, + "minimum": 1, + "type": "integer" + } + }, + { + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "in": "query", + "name": "page_token", + "schema": { + "default": "1", + "minimum": 1, + "type": "string" + } + }, + { + "description": "The subject to list the consent sessions for.", + "in": "query", + "name": "subject", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The login session id to list the consent sessions for.", + "in": "query", + "name": "login_session_id", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2ConsentSessions" + } + } + }, + "description": "oAuth2ConsentSessions" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "List OAuth 2.0 Consent Sessions of a Subject", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/sessions/login": { + "delete": { + "description": "This endpoint invalidates authentication sessions. After revoking the authentication session(s), the subject\nhas to re-authenticate at the Ory OAuth2 Provider. This endpoint does not invalidate any tokens.\n\nIf you send the subject in a query param, all authentication sessions that belong to that subject are revoked.\nNo OpennID Connect Front- or Back-channel logout is performed in this case.\n\nAlternatively, you can send a SessionID via `sid` query param, in which case, only the session that is connected\nto that SessionID is revoked. OpenID Connect Back-channel logout is performed in this case.", + "operationId": "revokeOAuth2LoginSessions", + "parameters": [ + { + "description": "OAuth 2.0 Subject\n\nThe subject to revoke authentication sessions for.", + "in": "query", + "name": "subject", + "schema": { + "type": "string" + } + }, + { + "description": "OAuth 2.0 Subject\n\nThe subject to revoke authentication sessions for.", + "in": "query", + "name": "sid", + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Revokes OAuth 2.0 Login Sessions by either a Subject or a SessionID", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/introspect": { + "post": { + "description": "The introspection endpoint allows to check if a token (both refresh and access) is active or not. An active token\nis neither expired nor revoked. If a token is active, additional information on the token will be included. You can\nset additional data for a token by setting `session.access_token` during the consent flow.", + "operationId": "introspectOAuth2Token", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "properties": { + "scope": { + "description": "An optional, space separated list of required scopes. If the access token was not granted one of the\nscopes, the result of active will be false.", + "type": "string", + "x-formData-name": "scope" + }, + "token": { + "description": "The string value of the token. For access tokens, this\nis the \"access_token\" value returned from the token endpoint\ndefined in OAuth 2.0. For refresh tokens, this is the \"refresh_token\"\nvalue returned.", + "required": [ + "token" + ], + "type": "string", + "x-formData-name": "token" + } + }, + "required": [ + "token" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/introspectedOAuth2Token" + } + } + }, + "description": "introspectedOAuth2Token" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Introspect OAuth2 Access and Refresh Tokens", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/tokens": { + "delete": { + "description": "This endpoint deletes OAuth2 access tokens issued to an OAuth 2.0 Client from the database.", + "operationId": "deleteOAuth2Token", + "parameters": [ + { + "description": "OAuth 2.0 Client ID", + "in": "query", + "name": "client_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Delete OAuth 2.0 Access Tokens from specific OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/trust/grants/jwt-bearer/issuers": { + "get": { + "description": "Use this endpoint to list all trusted JWT Bearer Grant Type Issuers.", + "operationId": "listTrustedOAuth2JwtGrantIssuers", + "parameters": [ + { + "in": "query", + "name": "MaxItems", + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "in": "query", + "name": "DefaultItems", + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "If optional \"issuer\" is supplied, only jwt-bearer grants with this issuer will be returned.", + "in": "query", + "name": "issuer", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantIssuers" + } + } + }, + "description": "trustedOAuth2JwtGrantIssuers" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "List Trusted OAuth2 JWT Bearer Grant Type Issuers", + "tags": [ + "oAuth2" + ] + }, + "post": { + "description": "Use this endpoint to establish a trust relationship for a JWT issuer\nto perform JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication\nand Authorization Grants [RFC7523](https://datatracker.ietf.org/doc/html/rfc7523).", + "operationId": "trustOAuth2JwtGrantIssuer", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/trustOAuth2JwtGrantIssuer" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantIssuer" + } + } + }, + "description": "trustedOAuth2JwtGrantIssuer" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Trust OAuth2 JWT Bearer Grant Type Issuer", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/trust/grants/jwt-bearer/issuers/{id}": { + "delete": { + "description": "Use this endpoint to delete trusted JWT Bearer Grant Type Issuer. The ID is the one returned when you\ncreated the trust relationship.\n\nOnce deleted, the associated issuer will no longer be able to perform the JSON Web Token (JWT) Profile\nfor OAuth 2.0 Client Authentication and Authorization Grant.", + "operationId": "deleteTrustedOAuth2JwtGrantIssuer", + "parameters": [ + { + "description": "The id of the desired grant", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Delete Trusted OAuth2 JWT Bearer Grant Type Issuer", + "tags": [ + "oAuth2" + ] + }, + "get": { + "description": "Use this endpoint to get a trusted JWT Bearer Grant Type Issuer. The ID is the one returned when you\ncreated the trust relationship.", + "operationId": "getTrustedOAuth2JwtGrantIssuer", + "parameters": [ + { + "description": "The id of the desired grant", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantIssuer" + } + } + }, + "description": "trustedOAuth2JwtGrantIssuer" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Get Trusted OAuth2 JWT Bearer Grant Type Issuer", + "tags": [ + "oAuth2" + ] + } + }, + "/health/alive": { + "get": { + "description": "This endpoint returns a HTTP 200 status code when Ory Hydra is accepting incoming\nHTTP requests. This status does currently not include checks whether the database connection is working.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the health status will never\nrefer to the cluster state, only to a single instance.", + "operationId": "isAlive", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/healthStatus" + } + } + }, + "description": "Ory Hydra is ready to accept connections." + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Check HTTP Server Status", + "tags": [ + "metadata" + ] + } + }, + "/health/ready": { + "get": { + "description": "This endpoint returns a HTTP 200 status code when Ory Hydra is up running and the environment dependencies (e.g.\nthe database) are responsive as well.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of Ory Hydra, the health status will never\nrefer to the cluster state, only to a single instance.", + "operationId": "isReady", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "description": "Always \"ok\".", + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "Ory Hydra is ready to accept requests." + }, + "503": { + "content": { + "application/json": { + "schema": { + "properties": { + "errors": { + "additionalProperties": { + "type": "string" + }, + "description": "Errors contains a list of errors that caused the not ready status.", + "type": "object" + } + }, + "type": "object" + } + } + }, + "description": "Ory Kratos is not yet ready to accept requests." + } + }, + "summary": "Check HTTP Server and Database Status", + "tags": [ + "metadata" + ] + } + }, + "/oauth2/auth": { + "get": { + "description": "Use open source libraries to perform OAuth 2.0 and OpenID Connect\navailable for any programming language. You can find a list of libraries at https://oauth.net/code/\n\nThe Ory SDK is not yet able to this endpoint properly.", + "operationId": "oAuth2Authorize", + "responses": { + "302": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "OAuth 2.0 Authorize Endpoint", + "tags": [ + "oAuth2" + ] + } + }, + "/oauth2/register": { + "post": { + "description": "This endpoint behaves like the administrative counterpart (`createOAuth2Client`) but is capable of facing the\npublic internet directly and can be used in self-service. It implements the OpenID Connect\nDynamic Client Registration Protocol. This feature needs to be enabled in the configuration. This endpoint\nis disabled by default. It can be enabled by an administrator.\n\nPlease note that using this endpoint you are not able to choose the `client_secret` nor the `client_id` as those\nvalues will be server generated when specifying `token_endpoint_auth_method` as `client_secret_basic` or\n`client_secret_post`.\n\nThe `client_secret` will be returned in the response and you will not be able to retrieve it later on.\nWrite the secret down and keep it somewhere safe.", + "operationId": "createOidcDynamicClient", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "Dynamic Client Registration Request Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "400": { + "$ref": "#/components/responses/errorOAuth2BadRequest" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Register OAuth2 Client using OpenID Dynamic Client Registration", + "tags": [ + "oidc" + ] + } + }, + "/oauth2/register/{id}": { + "delete": { + "description": "This endpoint behaves like the administrative counterpart (`deleteOAuth2Client`) but is capable of facing the\npublic internet directly and can be used in self-service. It implements the OpenID Connect\nDynamic Client Registration Protocol. This feature needs to be enabled in the configuration. This endpoint\nis disabled by default. It can be enabled by an administrator.\n\nTo use this endpoint, you will need to present the client's authentication credentials. If the OAuth2 Client\nuses the Token Endpoint Authentication Method `client_secret_post`, you need to present the client secret in the URL query.\nIf it uses `client_secret_basic`, present the Client ID and the Client Secret in the Authorization header.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "deleteOidcDynamicClient", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Delete OAuth 2.0 Client using the OpenID Dynamic Client Registration Management Protocol", + "tags": [ + "oidc" + ] + }, + "get": { + "description": "This endpoint behaves like the administrative counterpart (`getOAuth2Client`) but is capable of facing the\npublic internet directly and can be used in self-service. It implements the OpenID Connect\nDynamic Client Registration Protocol.\n\nTo use this endpoint, you will need to present the client's authentication credentials. If the OAuth2 Client\nuses the Token Endpoint Authentication Method `client_secret_post`, you need to present the client secret in the URL query.\nIf it uses `client_secret_basic`, present the Client ID and the Client Secret in the Authorization header.", + "operationId": "getOidcDynamicClient", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Get OAuth2 Client using OpenID Dynamic Client Registration", + "tags": [ + "oidc" + ] + }, + "put": { + "description": "This endpoint behaves like the administrative counterpart (`setOAuth2Client`) but is capable of facing the\npublic internet directly to be used by third parties. It implements the OpenID Connect\nDynamic Client Registration Protocol.\n\nThis feature is disabled per default. It can be enabled by a system administrator.\n\nIf you pass `client_secret` the secret is used, otherwise the existing secret is used. If set, the secret is echoed in the response.\nIt is not possible to retrieve it later on.\n\nTo use this endpoint, you will need to present the client's authentication credentials. If the OAuth2 Client\nuses the Token Endpoint Authentication Method `client_secret_post`, you need to present the client secret in the URL query.\nIf it uses `client_secret_basic`, present the Client ID and the Client Secret in the Authorization header.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "setOidcDynamicClient", + "parameters": [ + { + "description": "OAuth 2.0 Client ID", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "OAuth 2.0 Client Request Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "404": { + "$ref": "#/components/responses/errorOAuth2NotFound" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Set OAuth2 Client using OpenID Dynamic Client Registration", + "tags": [ + "oidc" + ] + } + }, + "/oauth2/revoke": { + "post": { + "description": "Revoking a token (both access and refresh) means that the tokens will be invalid. A revoked access token can no\nlonger be used to make access requests, and a revoked refresh token can no longer be used to refresh an access token.\nRevoking a refresh token also invalidates the access token that was created with it. A token may only be revoked by\nthe client the token was generated for.", + "operationId": "revokeOAuth2Token", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "properties": { + "client_id": { + "type": "string", + "x-formData-name": "client_id" + }, + "client_secret": { + "type": "string", + "x-formData-name": "client_secret" + }, + "token": { + "required": [ + "token" + ], + "type": "string", + "x-formData-name": "token" + } + }, + "required": [ + "token" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "security": [ + { + "basic": [] + }, + { + "oauth2": [] + } + ], + "summary": "Revoke OAuth 2.0 Access or Refresh Token", + "tags": [ + "oAuth2" + ] + } + }, + "/oauth2/sessions/logout": { + "get": { + "description": "This endpoint initiates and completes user logout at the Ory OAuth2 & OpenID provider and initiates OpenID Connect Front- / Back-channel logout:\n\nhttps://openid.net/specs/openid-connect-frontchannel-1_0.html\nhttps://openid.net/specs/openid-connect-backchannel-1_0.html\n\nBack-channel logout is performed asynchronously and does not affect logout flow.", + "operationId": "revokeOidcSession", + "responses": { + "302": { + "$ref": "#/components/responses/emptyResponse" + } + }, + "summary": "OpenID Connect Front- and Back-channel Enabled Logout", + "tags": [ + "oidc" + ] + } + }, + "/oauth2/token": { + "post": { + "description": "Use open source libraries to perform OAuth 2.0 and OpenID Connect\navailable for any programming language. You can find a list of libraries here https://oauth.net/code/\n\nThe Ory SDK is not yet able to this endpoint properly.", + "operationId": "oauth2TokenExchange", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "properties": { + "client_id": { + "type": "string", + "x-formData-name": "client_id" + }, + "code": { + "type": "string", + "x-formData-name": "code" + }, + "grant_type": { + "required": [ + "grant_type" + ], + "type": "string", + "x-formData-name": "grant_type" + }, + "redirect_uri": { + "type": "string", + "x-formData-name": "redirect_uri" + }, + "refresh_token": { + "type": "string", + "x-formData-name": "refresh_token" + } + }, + "required": [ + "grant_type" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2TokenExchange" + } + } + }, + "description": "oAuth2TokenExchange" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "security": [ + { + "basic": [] + }, + { + "oauth2": [] + } + ], + "summary": "The OAuth 2.0 Token Endpoint", + "tags": [ + "oAuth2" + ] + } + }, + "/userinfo": { + "get": { + "description": "This endpoint returns the payload of the ID Token, including `session.id_token` values, of\nthe provided OAuth 2.0 Access Token's consent request.\n\nIn the case of authentication error, a WWW-Authenticate header might be set in the response\nwith more information about the error. See [the spec](https://datatracker.ietf.org/doc/html/rfc6750#section-3)\nfor more details about header format.", + "operationId": "getOidcUserInfo", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oidcUserInfo" + } + } + }, + "description": "oidcUserInfo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "security": [ + { + "oauth2": [] + } + ], + "summary": "OpenID Connect Userinfo", + "tags": [ + "oidc" + ] + } + }, + "/version": { + "get": { + "description": "This endpoint returns the version of Ory Hydra.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the version will never\nrefer to the cluster state, only to a single instance.", + "operationId": "getVersion", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "version": { + "description": "The version of Ory Hydra.", + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "Returns the Ory Hydra version." + } + }, + "summary": "Return Running Software Version.", + "tags": [ + "metadata" + ] + } + } + }, + "tags": [ + { + "description": "OAuth 2.0", + "name": "oAuth2" + }, + { + "description": "OpenID Connect", + "name": "oidc" + }, + { + "description": "JSON Web Keys", + "name": "jwk" + }, + { + "description": "Well-Known Endpoints", + "name": "wellknown" + }, + { + "description": "Service Metadata", + "name": "metadata" + } + ], + "x-forwarded-proto": "string", + "x-request-id": "string" +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/test_and_report.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/test_and_report.bash new file mode 100644 index 0000000..ef6d5fe --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-backend/test_and_report.bash @@ -0,0 +1,3 @@ +#!/bin/bash + +./gradlew test jacocoTestReport diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/.editorconfig b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/.editorconfig new file mode 100644 index 0000000..59d9a3a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/.gitignore b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/.gitignore new file mode 100644 index 0000000..1f4031f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/.gitignore @@ -0,0 +1,44 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings + +# System files +.DS_Store +Thumbs.db + +package-lock.json diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/angular.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/angular.json new file mode 100644 index 0000000..934de8c --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/angular.json @@ -0,0 +1,125 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "authorization-frontend": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:application", + "options": { + "outputPath": "dist/authorization-frontend", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": [ + "zone.js" + ], + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "src/favicon.ico", + "src/assets", + "src/fonts" + ], + "styles": [ + "src/styles.scss" + ], + "scripts": [], + "server": "src/main.server.ts", + "prerender": true, + "ssr": { + "entry": "server.ts" + } + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "authorization-frontend:build:production" + }, + "development": { + "buildTarget": "authorization-frontend:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "authorization-frontend:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "polyfills": [ + "zone.js", + "zone.js/testing" + ], + "tsConfig": "tsconfig.spec.json", + "inlineStyleLanguage": "scss", + "assets": [ + "src/favicon.ico", + "src/assets", + "src/fonts" + ], + "styles": [ + "src/styles.scss" + ], + "scripts": [], + "karmaConfig": "karma.conf.js", + "codeCoverageExclude": [ + "src/app/window.service.ts", + "src/app/app.component.ts" + ] + } + } + } + } + }, + "cli": { + "cache": { + "path": "/tmp/.angular/cache" + }, + "analytics": false + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/build_image.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/build_image.bash new file mode 100644 index 0000000..6834a81 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/build_image.bash @@ -0,0 +1,47 @@ +#!/bin/bash + +# export HTTP_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +# export HTTPS_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +# export NO_PROXY="localhost,127.0.0.1" + +export REPO_IMAGE="chistousov" +export PROJECT_NAME="ory-hydra-oauth2-example-authorization-server-frontend" +export VERSION="1.0.0" + +# install pack +# https://buildpacks.io/docs/tools/pack/#linux-script-install +# (curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.29.0/pack-v0.29.0-linux.tgz" | sudo tar -C /usr/local/bin/ --no-same-owner -xzv pack) + +npm i + +ng test --no-watch --code-coverage --browsers Firefox +rm -rf dist/ || true +npm run build + +docker pull paketobuildpacks/builder-jammy-full:0.3.316 + +rm -rf app_server/ || true +mkdir app_server +mv dist/ app_server/dist + +pack -v \ +--path app_server \ +build \ +$REPO_IMAGE/$PROJECT_NAME:$VERSION \ +--env HTTP_PROXY="$HTTP_PROXY" \ +--env HTTPS_PROXY="$HTTPS_PROXY" \ +--env NO_PROXY="$NO_PROXY" \ +--env BP_NODE_OPTIMIZE_MEMORY=true \ +--env BP_HEALTH_CHECKER_ENABLED=true \ +--env BP_LAUNCHPOINT="dist/authorization-frontend/server/server.mjs" \ +--buildpack gcr.io/paketo-buildpacks/nodejs \ +--buildpack gcr.io/paketo-buildpacks/health-checker:latest \ +--builder paketobuildpacks/builder-jammy-full:0.3.316 + +rm -rf app_server + +# publish in docker hub +# docker login +# docker push $REPO_IMAGE/$PROJECT_NAME:$VERSION +# docker logout +# docker rmi $REPO_IMAGE/$PROJECT_NAME:$VERSION diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/karma.conf.js b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/karma.conf.js new file mode 100644 index 0000000..ec05f3c --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/karma.conf.js @@ -0,0 +1,40 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma'), + require("karma-firefox-launcher") + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, './coverage/authorization-frontend'), + subdir: '.', + reporters: [ + { type: 'html' }, + { type: 'text-summary' } + ] + }, + reporters: ['progress', 'kjhtml'], + browsers: ['Firefox'], + restartOnFileChange: true + }); +}; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/mock-data.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/mock-data.json new file mode 100644 index 0000000..48c67be --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/mock-data.json @@ -0,0 +1,16 @@ +{ + "data":[ + { + "x": 1, + "y": 2.3 + }, + { + "x": 2, + "y": 5.6 + }, + { + "x": 3, + "y": 4.7 + } + ] + } diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/mock-server.js b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/mock-server.js new file mode 100644 index 0000000..1cd22fb --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/mock-server.js @@ -0,0 +1,60 @@ +const jsonServer = require('json-server'); +const server = jsonServer.create(); +const router = jsonServer.router('mock-data.json'); +const middlewares = jsonServer.defaults(); + +server.use(middlewares); + +server.post('/login', (req, res) => { + res.status(200).jsonp({ + redirect_to: "/consent" + }) +}) + +server.get('/somesome', (req, res) => { + res.status(200).jsonp({ + "test": "aaaaaaaaaaaaaaaaaaaaaaaa!" + }) +}) + +server.get('/consent/scopes', (req, res) => { + res.status(200).jsonp(["read", "write"]) +}) + +server.get('/consent/subject', (req, res) => { + res.status(200).jsonp('So Good Man') +}) + +server.get('/consent/client-name', (req, res) => { + res.status(200).jsonp('Some App') +}) + +server.put('/consent', (req, res) => { + res.status(200).jsonp({ + redirect_to: "some_redirect" + }); +}) + +server.delete('/consent/cancel', (req, res) => { + res.status(200).jsonp({ + redirect_to: "some_redirect" + }); +}) + +server.put('/logout', (req, res) => { + res.status(200).jsonp({ + redirect_to: "/logout/sssaaa" + }); +}) + +server.post('/registration', (req, res) => { + res.status(200).jsonp({}); +}) + + + +server.use(router); + +server.listen(3000, () => { + console.log('JSON Server is running'); +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/package.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/package.json new file mode 100644 index 0000000..9a2e147 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/package.json @@ -0,0 +1,49 @@ +{ + "name": "authorization-frontend", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test", + "dev:mock": "node mock-server.js", + "serve:ssr:authorization-frontend": "ng build --configuration development && node dist/authorization-frontend/server/server.mjs" + }, + "private": true, + "dependencies": { + "@angular/animations": "^17.0.0", + "@angular/cdk": "^17.0.1", + "@angular/common": "^17.0.0", + "@angular/compiler": "^17.0.0", + "@angular/core": "^17.0.0", + "@angular/forms": "^17.0.0", + "@angular/material": "^17.0.1", + "@angular/platform-browser": "^17.0.0", + "@angular/platform-browser-dynamic": "^17.0.0", + "@angular/platform-server": "^17.0.0", + "@angular/router": "^17.0.0", + "@angular/ssr": "^17.0.1", + "express": "^4.18.2", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.14.2" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^17.0.1", + "@angular/cli": "~17.0.1", + "@angular/compiler-cli": "^17.0.0", + "@types/express": "^4.17.17", + "@types/jasmine": "~5.1.0", + "@types/node": "^18.18.0", + "jasmine-core": "~5.1.0", + "json-server": "^0.17.4", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-firefox-launcher": "^2.1.2", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.2.2" + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/server.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/server.ts new file mode 100644 index 0000000..96014ed --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/server.ts @@ -0,0 +1,60 @@ +import { APP_BASE_HREF } from '@angular/common'; +import { CommonEngine } from '@angular/ssr'; +import express from 'express'; +import { fileURLToPath } from 'node:url'; +import { dirname, join, resolve } from 'node:path'; +import bootstrap from './src/main.server'; + +// The Express app is exported so that it can be used by serverless Functions. +export function app(): express.Express { + const server = express(); + const serverDistFolder = dirname(fileURLToPath(import.meta.url)); + const browserDistFolder = resolve(serverDistFolder, '../browser'); + const indexHtml = join(serverDistFolder, 'index.server.html'); + + const commonEngine = new CommonEngine(); + + server.set('view engine', 'html'); + server.set('views', browserDistFolder); + + server.get('/health', (req, res) => { + res.status(200).send('Ok'); + }); + + // Example Express Rest API endpoints + // server.get('/api/**', (req, res) => { }); + // Serve static files from /browser + server.get('*.*', express.static(browserDistFolder, { + maxAge: '1y' + })); + + // All regular routes use the Angular engine + server.get('*', (req, res, next) => { + const { protocol, originalUrl, baseUrl, headers } = req; + + commonEngine + .render({ + bootstrap, + documentFilePath: indexHtml, + url: `${protocol}://${headers.host}${originalUrl}`, + publicPath: browserDistFolder, + providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], + }) + .then((html) => res.send(html)) + .catch((err) => next(err)); + }); + + return server; +} + +function run(): void { + const port = process.env['PORT'] || 4000; + + // Start up the Node server + const server = app(); + server.listen(port, () => { + console.log(`Node Express server listening on http://localhost:${port}`); + }); +} + +run(); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.component.html new file mode 100644 index 0000000..90c6b64 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.component.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.component.scss new file mode 100644 index 0000000..00608b4 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.component.scss @@ -0,0 +1,7 @@ +:host { + min-width: inherit; + width: inherit; + min-height: inherit; + height: inherit; + display: block; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.component.ts new file mode 100644 index 0000000..1b9a974 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.component.ts @@ -0,0 +1,32 @@ +import { CommonModule } from '@angular/common'; +import { Component, HostListener, QueryList, ViewChildren } from '@angular/core'; +import { MatTooltip } from '@angular/material/tooltip'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + selector: 'app-root', + standalone: true, + imports: [CommonModule, RouterOutlet], + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'] +}) +export class AppComponent { + + @ViewChildren(MatTooltip) allMatTooltip !: QueryList; + + @HostListener('window:keydown', ['$event']) onKeydownHandler(event: KeyboardEvent) { + if (!event.ctrlKey) { return; } + + if (event.metaKey || event?.key === 'OS') { + if (this.allMatTooltip) { + this.allMatTooltip.forEach(matTooltip => matTooltip.show()); + setTimeout(() => { + this.allMatTooltip.forEach(matTooltip => matTooltip.hide()); + }, 3000); + } + return false; + } + return; + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.config.server.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.config.server.ts new file mode 100644 index 0000000..e16171f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.config.server.ts @@ -0,0 +1,11 @@ +import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; +import { provideServerRendering } from '@angular/platform-server'; +import { appConfig } from './app.config'; + +const serverConfig: ApplicationConfig = { + providers: [ + provideServerRendering() + ] +}; + +export const config = mergeApplicationConfig(appConfig, serverConfig); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.config.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.config.ts new file mode 100644 index 0000000..cad68d6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.config.ts @@ -0,0 +1,29 @@ +import { ApplicationConfig, importProvidersFrom } from '@angular/core'; +import { provideRouter } from '@angular/router'; + +import { routes } from './app.routes'; +import { provideAnimations } from '@angular/platform-browser/animations'; +import { HttpClientModule } from '@angular/common/http'; +import { provideClientHydration } from '@angular/platform-browser'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatButtonModule } from '@angular/material/button'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideRouter(routes), + provideAnimations(), + importProvidersFrom( + HttpClientModule, + FormsModule, + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule, + MatTooltipModule, + MatButtonModule + ), provideClientHydration() + ] +}; + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.routes.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.routes.ts new file mode 100644 index 0000000..2f084bf --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.routes.ts @@ -0,0 +1,35 @@ +import { Routes } from '@angular/router'; +import { NotFoundComponent } from './not-found/not-found.component'; + +export const routes: Routes = [ + { + path: 'error', + loadComponent: () => import("./error/error.component").then(c => c.ErrorComponent) + }, + { path: 'registration', loadChildren: () => import('./registration/registration.module').then(m => m.RegistrationModule) }, + { + path: '', + redirectTo: '/registration', + pathMatch: 'full' + }, + { + path: 'login', + loadComponent: () => import("./login/login.component").then(c => c.LoginComponent) + }, + { + path: 'consent', + loadComponent: () => import("./consent/consent.component").then(c => c.ConsentComponent) + }, + { + path: "logout/request", + loadComponent: () => import("./logout-request/logout-request.component").then(c => c.LogoutRequestComponent) + }, + { + path: "logout/:status", + loadComponent: () => import("./logout-handled/logout-handled.component").then(c => c.LogoutHandledComponent) + }, + { + path: '**', + component: NotFoundComponent + } +]; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.service.spec.ts new file mode 100644 index 0000000..7da7dab --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.service.spec.ts @@ -0,0 +1,18 @@ +import { TestBed } from '@angular/core/testing'; + +import { AppService } from './app.service'; + +describe('AppService', () => { + let service: AppService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AppService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + + expect(service.generateRandomString(20)).toBeTruthy(); + }); +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.service.ts new file mode 100644 index 0000000..c864b18 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/app.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class AppService { + + generateRandomString(length: number): string { + + let result: string = ''; + let characters: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let charactersLength: number = characters.length; + + for (let i: number = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + + return result; + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.component.html new file mode 100644 index 0000000..2935342 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.component.html @@ -0,0 +1,48 @@ +
+ +
+ + + + + +
+ Hi {{ subject }}, application {{ clientName }} wants access resources on your + behalf + and to: +
+ + + @for (scope of scopes; track scope; let idx = $index) { + {{ scope }} + @if(idx !== (scopes.length - 1)){ + + } + } + + +
+ Do you want to be asked next time when this application wants to access your data? The application + will + not be able to ask for more permissions without your consent. +
+ Do not ask me + again + +
+ + + +
+
+
+
+
\ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.component.scss new file mode 100644 index 0000000..48a8e95 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.component.scss @@ -0,0 +1,11 @@ +:host { + min-width: inherit; + width: inherit; + min-height: inherit; + height: inherit; + + display: flex; + flex-direction: column; + + margin: 1em; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.component.spec.ts new file mode 100644 index 0000000..459a79a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.component.spec.ts @@ -0,0 +1,288 @@ +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; + +import { ConsentComponent } from './consent.component'; +import { GenerationCookieCsrfService } from '../generation-cookie-csrf.service'; +import { ConsentService } from './consent.service'; +import { WindowService } from '../window.service'; +import { DebugElement } from '@angular/core'; +import { MatCheckboxHarness } from '@angular/material/checkbox/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { By } from '@angular/platform-browser'; +import { of, throwError } from 'rxjs'; +import { ResponseWithRedirectModel } from '../models/response-with-redirect.model'; +import { HttpErrorResponse } from '@angular/common/http'; + +describe('ConsentComponent', () => { + let generationCookieCsrfServiceSpy: jasmine.SpyObj; + let consentServiceSpy: jasmine.SpyObj; + let windowServiceSpy: jasmine.SpyObj; + + let component: ConsentComponent; + let fixture: ComponentFixture; + + let hostDe: DebugElement; + + let checkbox: MatCheckboxHarness; + let allowButton: HTMLButtonElement; + let denyButton: HTMLButtonElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ConsentComponent], + providers: [ + { + provide: GenerationCookieCsrfService, + useValue: jasmine.createSpyObj('GenerationCookieCsrfService', ['generateCookieCsrf']) + }, + { + provide: ConsentService, + useValue: jasmine.createSpyObj('ConsentService', ['clientName', 'scopes', 'subject', 'consentSuccess', 'consentCancel']) + }, + { + provide: WindowService, useValue: jasmine.createSpyObj('WindowService', ['get']) + } + ] + }) + .compileComponents() + .then(async () => { + generationCookieCsrfServiceSpy = TestBed.inject(GenerationCookieCsrfService) as jasmine.SpyObj; + consentServiceSpy = TestBed.inject(ConsentService) as jasmine.SpyObj; + windowServiceSpy = TestBed.inject(WindowService) as jasmine.SpyObj; + + const clientName: string = 'Some client'; + const subject: string = 'Some Subject'; + const scopes: string[] = ['read', "write"]; + + consentServiceSpy.clientName.and.returnValue(of(clientName)); + consentServiceSpy.subject.and.returnValue(of(subject)); + consentServiceSpy.scopes.and.returnValue(of(scopes)); + + fixture = TestBed.createComponent(ConsentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + + let loader: HarnessLoader = TestbedHarnessEnvironment.loader(fixture) + + checkbox = await loader.getHarness(MatCheckboxHarness.with({ name: 'isRemember' })); + + hostDe = fixture.debugElement; + + allowButton = hostDe.query(By.css("#allowButton")).nativeElement; + denyButton = hostDe.query(By.css("#denyButton")).nativeElement; + + }); + + }); + + + it('consent should be succeed (GUI)', fakeAsync(() => { + // given (instead of when) + + const responseWithRedirectModel: ResponseWithRedirectModel = { + redirect_to: "/some_url" + }; + consentServiceSpy.consentSuccess.and.returnValue(of(responseWithRedirectModel)); + + const windowObj = { + "location": { + "href": {} + } + } + windowServiceSpy.get.and.returnValue(windowObj); + + // when + + checkbox.toggle(); + tick(); + + fixture.detectChanges(); + expect(component.isRemember.value).withContext("component.isRemember === true").toEqual(true); + + allowButton.click(); + tick(); + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(consentServiceSpy.consentSuccess.calls.count()) + .withContext("consentServiceSpy.consentSuccess.calls.count() !== 1") + .toBe(1); + expect(consentServiceSpy.consentSuccess.calls.first().args) + .withContext(`consentServiceSpy.consentSuccess.calls.first().args != [${true} ]`) + .toEqual([true]); + + expect(windowServiceSpy.get.calls.count()) + .withContext("windowServiceSpy.get.calls.count() !== 1") + .toBe(1); + + })); + + it('consent should be succeed (GUI) (enter)', fakeAsync(() => { + // given (instead of when) + + const responseWithRedirectModel: ResponseWithRedirectModel = { + redirect_to: "/some_url" + }; + consentServiceSpy.consentSuccess.and.returnValue(of(responseWithRedirectModel)); + + const windowObj = { + "location": { + "href": {} + } + } + windowServiceSpy.get.and.returnValue(windowObj); + + // when + + // enter + window.dispatchEvent(new KeyboardEvent('keydown', { + key: 'enter' + })); + fixture.detectChanges(); + + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(consentServiceSpy.consentSuccess.calls.count()) + .withContext("consentServiceSpy.consentSuccess.calls.count() !== 1") + .toBe(1); + expect(consentServiceSpy.consentSuccess.calls.first().args) + .withContext(`consentServiceSpy.consentSuccess.calls.first().args != [${false} ]`) + .toEqual([false]); + + expect(windowServiceSpy.get.calls.count()) + .withContext("windowServiceSpy.get.calls.count() !== 1") + .toBe(1); + })); + + + it('deny (GUI)', fakeAsync(() => { + // given (instead of when) + + const responseWithRedirectModel: ResponseWithRedirectModel = { + redirect_to: "/some_url" + }; + consentServiceSpy.consentCancel.and.returnValue(of(responseWithRedirectModel)); + + const windowObj = { + "location": { + "href": {} + } + } + windowServiceSpy.get.and.returnValue(windowObj); + + // when + + denyButton.click(); + tick(); + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(consentServiceSpy.consentCancel.calls.count()) + .withContext("consentServiceSpy.consentCancel.calls.count() !== 1") + .toBe(1); + + expect(windowServiceSpy.get.calls.count()) + .withContext("windowServiceSpy.get.calls.count() !== 1") + .toBe(1); + })); + + it('deny (GUI) (esc)', fakeAsync(() => { + // given (instead of when) + + const responseWithRedirectModel: ResponseWithRedirectModel = { + redirect_to: "/some_url" + }; + consentServiceSpy.consentCancel.and.returnValue(of(responseWithRedirectModel)); + + const windowObj = { + "location": { + "href": {} + } + } + windowServiceSpy.get.and.returnValue(windowObj); + + // when + + // enter + window.dispatchEvent(new KeyboardEvent('keydown', { + key: 'Escape', + })); + fixture.detectChanges(); + + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(consentServiceSpy.consentCancel.calls.count()) + .withContext("consentServiceSpy.consentCancel.calls.count() !== 1") + .toBe(1); + + expect(windowServiceSpy.get.calls.count()) + .withContext("windowServiceSpy.get.calls.count() !== 1") + .toBe(1); + })); + + it('consentSuccess (GUI) (esc) (fail)', fakeAsync(() => { + // given (instead of when) + + consentServiceSpy.consentSuccess.and.returnValue(throwError(() => new HttpErrorResponse({status: 404}))); + // when + + checkbox.toggle(); + tick(); + + fixture.detectChanges(); + expect(component.isRemember.value).withContext("component.isRemember === true").toEqual(true); + + allowButton.click(); + tick(); + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(consentServiceSpy.consentSuccess.calls.count()) + .withContext("consentServiceSpy.consentSuccess.calls.count() !== 1") + .toBe(1); + expect(consentServiceSpy.consentSuccess.calls.first().args) + .withContext(`consentServiceSpy.consentSuccess.calls.first().args != [${true} ]`) + .toEqual([true]); + + + })); + + it('deny (GUI) (fail)', fakeAsync(() => { + // given (instead of when) + + consentServiceSpy.consentCancel.and.returnValue(throwError(() => new HttpErrorResponse({status: 404}))); + // when + + denyButton.click(); + tick(); + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(consentServiceSpy.consentCancel.calls.count()) + .withContext("consentServiceSpy.consentCancel.calls.count() !== 1") + .toBe(1); + + })); + + +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.component.ts new file mode 100644 index 0000000..7ef357a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.component.ts @@ -0,0 +1,144 @@ +import { Component, HostListener, OnDestroy } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCheckboxModule } from '@angular/material/checkbox'; + +import { MatDividerModule } from '@angular/material/divider'; +import { MatListModule } from '@angular/material/list'; +import { Observable, Subject, first, takeUntil } from 'rxjs'; +import { ConsentService } from './consent.service'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { HttpErrorResponse } from '@angular/common/http'; +import { WindowService } from '../window.service'; +import { GenerationCookieCsrfService } from '../generation-cookie-csrf.service'; +import { MatTooltipModule } from '@angular/material/tooltip'; + +@Component({ + selector: 'app-consent', + standalone: true, + imports: [ + CommonModule, + MatButtonModule, + MatCheckboxModule, + MatDividerModule, + MatListModule, + ReactiveFormsModule, + MatProgressSpinnerModule, + MatTooltipModule + ], + templateUrl: './consent.component.html', + styleUrl: './consent.component.scss' +}) +export class ConsentComponent implements OnDestroy { + // to clear the rxjs memory + // для очистки памяти rxjs + private readonly onDestroy = new Subject(); + + // meta info + requestedScopes$: Observable; + subject$: Observable; + clientName$: Observable; + + readonly isRemember = new FormControl(false); + + // waiting for a response from the server + // ожидание ответа от сервера + getResponseFromServerFlag: boolean = false; + + constructor(private consentService: ConsentService, + private generationCookieCsrfService: GenerationCookieCsrfService, + private windowService: WindowService) { + + this.requestedScopes$ = this.consentService.scopes() + .pipe( + first(), + takeUntil(this.onDestroy) + ); + + this.subject$ = this.consentService.subject() + .pipe( + first(), + takeUntil(this.onDestroy) + ); + + this.clientName$ = this.consentService.clientName() + .pipe( + first(), + takeUntil(this.onDestroy) + ); + } + + @HostListener('window:keydown.enter', ['$event']) onKeydownEnterHandler(event: KeyboardEvent) { + this.allowAccess(!!this.isRemember.value); + return; + } + + @HostListener('window:keydown.esc', ['$event']) onKeydownEscHandler(event: KeyboardEvent) { + this.denyAccess(); + return; + } + + allowAccess(isRemember: boolean) { + if (!this.getResponseFromServerFlag) { + + this.getResponseFromServerFlag = true; + + this.generationCookieCsrfService.generateCookieCsrf(); + + this.consentService.consentSuccess(isRemember) + .pipe( + first(), + takeUntil(this.onDestroy) + ) + .subscribe({ + next: (responseLogin) => { + + this.windowService.get().location.href = responseLogin.redirect_to; + + }, + error: (error: HttpErrorResponse) => { + + // received response + // ответ получили + this.getResponseFromServerFlag = false; + } + }); + } + } + + denyAccess() { + + + if (!this.getResponseFromServerFlag) { + + this.getResponseFromServerFlag = true; + + this.generationCookieCsrfService.generateCookieCsrf(); + + this.consentService.consentCancel() + .pipe( + first(), + takeUntil(this.onDestroy) + ) + .subscribe({ + next: (responseLogin) => { + + this.windowService.get().location.href = responseLogin.redirect_to; + + }, + error: (error: HttpErrorResponse) => { + + // received response + // ответ получили + this.getResponseFromServerFlag = false; + } + }); + } + } + + ngOnDestroy(): void { + this.onDestroy.next(); + this.onDestroy.complete(); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.service.spec.ts new file mode 100644 index 0000000..ffd7168 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.service.spec.ts @@ -0,0 +1,246 @@ +import { TestBed } from '@angular/core/testing'; + +import { ConsentService } from './consent.service'; +import { ErrorService } from '../error.service'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { environment } from '../../environments/environment'; +import { ResponseWithRedirectModel } from '../models/response-with-redirect.model'; +import { HttpErrorResponse, HttpParams } from '@angular/common/http'; + +describe('ConsentService', () => { + let service: ConsentService; + let errorServiceSpy: jasmine.SpyObj; + let httpTestingController: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ HttpClientTestingModule ], + providers: [ + ConsentService, + { + provide: ErrorService, useValue: jasmine.createSpyObj('ErrorService', ['handle']) + } + ] + }); + service = TestBed.inject(ConsentService); + errorServiceSpy = TestBed.inject(ErrorService) as jasmine.SpyObj; + httpTestingController = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpTestingController.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + + it('clientName', (done: DoneFn) => { + // given (instead of when) + + const consentClientNameHttpURL = `${environment.apiUrl}/consent/client-name`; + + const clientNameFoo = 'Foo'; + + // when + + service.clientName() + .subscribe({ + next: clientN => { + expect(clientN).toEqual(clientNameFoo); + done(); + }, + error: er => done.fail('clientName is expected') + }); + + const req = httpTestingController.expectOne(consentClientNameHttpURL); + req.flush(clientNameFoo); + + // then (instead of verify) + + expect(req.request.method).toEqual('GET'); + }); + + it('subject', (done: DoneFn) => { + // given (instead of when) + + const subjectClientNameHttpURL = `${environment.apiUrl}/consent/subject`; + + const subject = 'qwerty'; + + // when + + service.subject() + .subscribe({ + next: sub => { + expect(sub).toEqual(subject); + done(); + }, + error: er => done.fail('subject is expected') + }); + + const req = httpTestingController.expectOne(subjectClientNameHttpURL); + req.flush(subject); + + // then (instead of verify) + + expect(req.request.method).toEqual('GET'); + }); + + it('scopes', (done: DoneFn) => { + // given (instead of when) + + const scopesHttpURL = `${environment.apiUrl}/consent/scopes`; + + const scopes: string[] = ['read', 'write']; + + // when + + service.scopes() + .subscribe({ + next: scops => { + expect(scops).toEqual(scopes); + done(); + }, + error: er => done.fail('scopes is expected') + }); + + const req = httpTestingController.expectOne(scopesHttpURL); + req.flush(scopes); + + // then (instead of verify) + + expect(req.request.method).toEqual('GET'); + }); + + it('consentSuccess', (done: DoneFn) => { + // given (instead of when) + + const consentSuccessHttpURL = `${environment.apiUrl}/consent`; + + const responseWithRedirectModel: ResponseWithRedirectModel = { + redirect_to: "some_redirect" + }; + + // when + + service.consentSuccess() + .subscribe({ + next: responseWithRedirectModelLocal => { + expect(responseWithRedirectModelLocal).toEqual(responseWithRedirectModel); + done(); + }, + error: er => done.fail('responseWithRedirectModelLocal is expected') + }); + + const req = httpTestingController.expectOne(consentSuccessHttpURL); + req.flush(responseWithRedirectModel); + + // then (instead of verify) + + expect(req.request.method).toEqual('PUT'); + }); + + + it('consentSuccess', (done: DoneFn) => { + // given (instead of when) + + const consentSuccessHttpURL = `${environment.apiUrl}/consent?is-remember=true`; + + let params = new HttpParams(); + params = params.append('is-remember', true); + + const responseWithRedirectModel: ResponseWithRedirectModel = { + redirect_to: "some_redirect" + }; + + // when + + service.consentSuccess(true) + .subscribe({ + next: responseWithRedirectModelLocal => { + expect(responseWithRedirectModelLocal).toEqual(responseWithRedirectModel); + done(); + }, + error: er => done.fail('responseWithRedirectModelLocal is expected') + }); + + const req = httpTestingController.expectOne(consentSuccessHttpURL); + req.flush(responseWithRedirectModel); + + // then (instead of verify) + + expect(req.request.method).toEqual('PUT'); + expect(req.request.params.has("is-remember")).toEqual(true); + expect(req.request.params.get("is-remember")).toEqual('true'); + }); + + + it('consentCancel', (done: DoneFn) => { + // given (instead of when) + + const consentCancelHttpURL = `${environment.apiUrl}/consent/cancel`; + + const responseWithRedirectModel: ResponseWithRedirectModel = { + redirect_to: "some_redirect" + }; + + // when + + service.consentCancel() + .subscribe({ + next: responseWithRedirectModelLocal => { + expect(responseWithRedirectModelLocal).toEqual(responseWithRedirectModel); + done(); + }, + error: er => done.fail('responseWithRedirectModelLocal is expected') + }); + + const req = httpTestingController.expectOne(consentCancelHttpURL); + req.flush(responseWithRedirectModel); + + // then (instead of verify) + + expect(req.request.method).toEqual('DELETE'); + }); + + it('consentCancel should be fail', done => { + // given (instead of when) + + const consentCancelHttpURL = `${environment.apiUrl}/consent/cancel`; + + const expectedErrorResponse = new HttpErrorResponse({ + error: '["password is invalid"]', + url: consentCancelHttpURL, + status: 400, + statusText: 'Bad request' + }); + + // when + + service.consentCancel() + .subscribe({ + next: responseWithRedirectModelLocal => done.fail('consentCancel not expected'), + error: (actualErrorResponse: HttpErrorResponse) => { + expect(actualErrorResponse).toEqual(expectedErrorResponse); + done(); + } + }); + + + const req = httpTestingController.expectOne(consentCancelHttpURL); + + req.flush(expectedErrorResponse.error, expectedErrorResponse); + + // then (instead of verify) + + expect(req.request.method).toEqual('DELETE'); + + expect(errorServiceSpy.handle.calls.count()) + .toBe(1); + expect(errorServiceSpy.handle.calls.first().args) + .toEqual([ expectedErrorResponse.error ]); + }); +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.service.ts new file mode 100644 index 0000000..552a993 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/consent/consent.service.ts @@ -0,0 +1,72 @@ +import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { ErrorService } from '../error.service'; +import { Observable, catchError, map, throwError } from 'rxjs'; +import { environment } from '../../environments/environment'; +import { ResponseWithRedirectModel } from '../models/response-with-redirect.model'; + +@Injectable({ + providedIn: 'root' +}) +export class ConsentService { + constructor(private http: HttpClient, private errorService: ErrorService) { } + + clientName(): Observable { + const requestOptions: Object = { + responseType: 'text' + } + + return this.http.get( + `${environment.apiUrl}/consent/client-name`, requestOptions) + .pipe( + catchError(this.errorHandler.bind(this)) + ); + } + + scopes(): Observable { + return this.http.get( + `${environment.apiUrl}/consent/scopes`) + .pipe( + catchError(this.errorHandler.bind(this)) + ); + } + + subject(): Observable { + const requestOptions: Object = { + responseType: 'text' + } + + return this.http.get( + `${environment.apiUrl}/consent/subject`, requestOptions) + .pipe( + catchError(this.errorHandler.bind(this)) + ); + } + + consentSuccess(isRemember: boolean = false): Observable { + let params = new HttpParams(); + + if(isRemember) { + params = params.append('is-remember', isRemember); + } + + return this.http.put( + `${environment.apiUrl}/consent`, {}, { params: params }) + .pipe( + catchError(this.errorHandler.bind(this)) + ); + } + + consentCancel(): Observable { + return this.http.delete( + `${environment.apiUrl}/consent/cancel`, {}) + .pipe( + catchError(this.errorHandler.bind(this)) + ); + } + + private errorHandler(error: HttpErrorResponse) { + this.errorService.handle(error.error) + return throwError(() => error) + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/cookie.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/cookie.service.spec.ts new file mode 100644 index 0000000..9ba91e0 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/cookie.service.spec.ts @@ -0,0 +1,61 @@ +import { TestBed } from '@angular/core/testing'; + +import { CookieService } from './cookie.service'; + +describe('CookieService', () => { + let service: CookieService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + CookieService + ] + }); + service = TestBed.inject(CookieService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('setCookie, getCookie and deleteCookie', () => { + // given (instead of when) + + const expectedNotExistCookieValue: string | undefined = undefined; + + const expectedSomeCookie = { + name: 'myCookieName', + value: 'myCookieValue', + options: { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax', + expires: new Date((new Date()).getTime() + 1) + } + }; + + const expectedSomeCookie2 = { + name: 'myCookieName2', + value: 'myCookieValue2' + }; + + // when + + const actualNotExistCookieValue = service.getCookie('notExistCookie'); + + service.setCookie(expectedSomeCookie.name, expectedSomeCookie.value, expectedSomeCookie.options); + const actualSomeCookieValue = service.getCookie(expectedSomeCookie.name); + + service.setCookie(expectedSomeCookie2.name, expectedSomeCookie2.value); + service.deleteCookie(expectedSomeCookie2.name); + const actualSomeCookieValue2 = service.getCookie(expectedSomeCookie2.name); + + // then (instead of verify) + + expect(actualNotExistCookieValue).toEqual(expectedNotExistCookieValue); + expect(actualSomeCookieValue).toEqual(expectedSomeCookie.value); + expect(actualSomeCookieValue2).toEqual(expectedNotExistCookieValue); + + }) +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/cookie.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/cookie.service.ts new file mode 100644 index 0000000..edf0fc6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/cookie.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class CookieService { + + // возвращает куки с указанным name, + // или undefined, если ничего не найдено + getCookie(name: string): string | undefined { + + let matches = document.cookie.match(new RegExp( + "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)" + )); + return matches ? decodeURIComponent(matches[1]) : undefined; + } + + setCookie(name: string, value: string, options: any = {} ) { + + options = { + path: '/', + // при необходимости добавьте другие значения по умолчанию + ...options + }; + + if (options.expires instanceof Date) { + options.expires = options.expires.toUTCString(); + } + + let updatedCookie = encodeURIComponent(name) + "=" + encodeURIComponent(value); + + for (let optionKey in options) { + updatedCookie += "; " + optionKey; + let optionValue = options[optionKey]; + if (optionValue !== true) { + updatedCookie += "=" + optionValue; + } + } + + document.cookie = updatedCookie; + } + + deleteCookie(name: string) { + this.setCookie(name, "", { + 'max-age': -1 + }) + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error.service.spec.ts new file mode 100644 index 0000000..b49e366 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error.service.spec.ts @@ -0,0 +1,50 @@ +import { TestBed } from '@angular/core/testing'; + +import { ErrorService } from './error.service'; +import { MatSnackBar, MatSnackBarConfig, MatSnackBarRef, TextOnlySnackBar } from '@angular/material/snack-bar'; + +describe('ErrorService', () => { + let service: ErrorService; + let matSnackBarSpy: jasmine.SpyObj; + + beforeEach(() => { + + TestBed.configureTestingModule({ + providers: [ + ErrorService, + { + provide: MatSnackBar, useValue: jasmine.createSpyObj('MatSnackBar', ['open']) + } + ] + }); + service = TestBed.inject(ErrorService); + matSnackBarSpy = TestBed.inject(MatSnackBar) as jasmine.SpyObj; + + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('handle', () => { + // given (instead of when) + + const expectedArgsOpen: [message: string, action?: string | undefined, config?: MatSnackBarConfig | undefined] = [ + 'Some Error', + service.actionCloseButtonName, + service.confOptionsSnackbar + ]; + + // when + + service.handle(expectedArgsOpen[0]); + + // then (instead of verify) + + expect(matSnackBarSpy.open.calls.count()) + .toBe(1); + expect(matSnackBarSpy.open.calls.first().args) + .toEqual(expectedArgsOpen); + }); + +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error.service.ts new file mode 100644 index 0000000..612f0bf --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error.service.ts @@ -0,0 +1,21 @@ +import {Injectable} from '@angular/core' +import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar' + +@Injectable({ + providedIn: 'root' +}) +export class ErrorService { + + readonly actionCloseButtonName = 'CLOSE'; + + readonly confOptionsSnackbar: MatSnackBarConfig = { + duration: 5000 + }; + + constructor(private matSnackBar: MatSnackBar){} + + handle(message: string) { + this.matSnackBar.open(message, this.actionCloseButtonName, this.confOptionsSnackbar); + } + +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error/error.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error/error.component.html new file mode 100644 index 0000000..d0528a6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error/error.component.html @@ -0,0 +1,16 @@ + + + logo +

Пришла ошибка от сервера аутентификации и авторизации

+ +

{{ errorOnPageModel.error }}

+ +

{{ errorOnPageModel.errorDescription }}

+ + + + cat_401 +
\ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error/error.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error/error.component.scss new file mode 100644 index 0000000..3b7d5c0 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error/error.component.scss @@ -0,0 +1,9 @@ +:host { + min-width: inherit; + width: inherit; + min-height: inherit; + height: inherit; + display: flex; + flex-direction: column; + align-items: center; +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error/error.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error/error.component.spec.ts new file mode 100644 index 0000000..b647724 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error/error.component.spec.ts @@ -0,0 +1,161 @@ +import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing'; + +import { RouterTestingHarness } from '@angular/router/testing'; + +import { ErrorComponent } from './error.component'; +import { ErrorOnPageModel } from '../models/error-on-page.model'; +import { ErrorResponseCode } from '../models/error-response-code.model'; +import { By } from '@angular/platform-browser'; +import { provideRouter } from '@angular/router'; +import { WindowService } from '../window.service'; + +describe('ErrorComponent', () => { + + let harness: RouterTestingHarness; + + let error: string = '111'; + let error_description: string = '222'; + let reauthentication_location: string = '/qqq'; + + let windowServiceSpy: jasmine.SpyObj; + + beforeEach(async () => { + + await TestBed.configureTestingModule({ + imports: [ErrorComponent], + providers: [ + provideRouter([ + { + path: 'error', + component: ErrorComponent + } + ]), + { + provide: WindowService, useValue: jasmine.createSpyObj('WindowService', ['get']) + }, + ] + }) + .compileComponents() + .then(async () => { + harness = await RouterTestingHarness.create(); + + windowServiceSpy = TestBed.inject(WindowService) as jasmine.SpyObj; + + }); + }); + + it('should create', done => { + harness.navigateByUrl(`/error`, ErrorComponent) + .then(comp => { + expect(comp).toBeTruthy(); + + comp + .errorOnPageModel$ + .subscribe( + { + next: el => { + + expect(`Неизвестная ошибка`).toEqual(el.error); + expect("Неизвестное описание").toEqual(el.errorDescription); + expect('/').toEqual(el.reauthenticationLocation); + + done(); + }, + error: er => done.fail("errorOnPageModel is incorrect") + }); + }); + + }); + + it('should create with query param', done => { + + harness.navigateByUrl(`/error?error=${error}&error_description=${error_description}&reauthentication_location=${reauthentication_location}`, ErrorComponent) + .then(compWithQueryParam => { + //when + expect(compWithQueryParam).toBeTruthy(); + + compWithQueryParam + .errorOnPageModel$ + .subscribe( + { + next: el => { + + expect(`Ошибка (${error})`).toEqual(el.error); + expect(error_description).toEqual(el.errorDescription); + expect(reauthentication_location).toEqual(el.reauthenticationLocation); + + done(); + }, + error: er => done.fail("errorOnPageModel is incorrect") + } + ); + + }); + + }); + + it('getErrorResponseCodeRus', async () => { + + // given (instead of when) + await harness.navigateByUrl(`/error`, ErrorComponent); + harness.detectChanges(); + + let h2 = harness.routeDebugElement!.query(By.css("h2")).nativeElement as HTMLParagraphElement; + + // when + expect(h2.textContent).toEqual("Неизвестная ошибка"); + + + let changeAndCheckError = async (currentErrorResponseCode: ErrorResponseCode) => { + await harness.navigateByUrl(`/`); + await harness.navigateByUrl(`/error?error=${currentErrorResponseCode}`, ErrorComponent); + harness.detectChanges(); + h2 = harness.routeDebugElement!.query(By.css("h2")).nativeElement as HTMLParagraphElement; + expect(h2.textContent).toEqual(ErrorOnPageModel.getErrorResponseCodeRus(currentErrorResponseCode)); + } + + await changeAndCheckError(ErrorResponseCode.AccessDenied); + await changeAndCheckError(ErrorResponseCode.InsufficientScope); + await changeAndCheckError(ErrorResponseCode.InvalidClient); + await changeAndCheckError(ErrorResponseCode.InvalidGrant); + await changeAndCheckError(ErrorResponseCode.InvalidRedirectUri); + await changeAndCheckError(ErrorResponseCode.InvalidRequest); + await changeAndCheckError(ErrorResponseCode.InvalidScope); + await changeAndCheckError(ErrorResponseCode.InvalidToken); + await changeAndCheckError(ErrorResponseCode.ServerError); + await changeAndCheckError(ErrorResponseCode.TemporarilyUnavailable); + await changeAndCheckError(ErrorResponseCode.UnauthorizedClient); + await changeAndCheckError(ErrorResponseCode.UnsupportedGrantType); + await changeAndCheckError(ErrorResponseCode.UnsupportedResponseType); + await changeAndCheckError(ErrorResponseCode.UnsupportedTokenType); + + }); + + + it('goToAuth', fakeAsync(async () => { + + // given (instead of when) + + const window = { + "location": { + "href": {} + } + } + + const comp = await harness.navigateByUrl(`/error?error=${error}&error_description=${error_description}&reauthentication_location=${reauthentication_location}`, ErrorComponent); + harness.detectChanges(); + + windowServiceSpy.get.and.returnValue(window); + + // when + expect(comp).toBeTruthy(); + + harness.detectChanges(); + let button = harness.routeDebugElement!.query(By.css("button[mat-raised-button]")).nativeElement as HTMLButtonElement; + button.click(); + harness.detectChanges(); + + expect(window.location.href).toEqual(reauthentication_location); + })); + +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error/error.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error/error.component.ts new file mode 100644 index 0000000..1deab86 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/error/error.component.ts @@ -0,0 +1,68 @@ +import { Component, OnDestroy } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Observable, Subject, first, map, takeUntil } from 'rxjs'; +import { ErrorOnPageModel } from '../models/error-on-page.model'; +import { ActivatedRoute } from '@angular/router'; + +import {MatButtonModule} from '@angular/material/button'; +import { WindowService } from '../window.service'; + +@Component({ + selector: 'app-error', + standalone: true, + imports: [CommonModule, MatButtonModule], + templateUrl: './error.component.html', + styleUrl: './error.component.scss' +}) +export class ErrorComponent implements OnDestroy { + // для очистки памяти rxjs + private readonly onDestroy = new Subject(); + + errorOnPageModel$: Observable; + + constructor( + private windowService: WindowService, + private route: ActivatedRoute + ) { + + this.errorOnPageModel$ = this.route.queryParamMap + .pipe( + first(), + map( + (paramMap) => { + let error: string | null = paramMap.get("error"); + let errorDescription: string | null = paramMap.get("error_description"); + let reauthenticationLocation: string | null = paramMap.get("reauthentication_location"); + + if (error) { + error = ErrorOnPageModel.getErrorResponseCodeRus(error); + } else { + error = "Неизвестная ошибка" + } + + if (!errorDescription) { + errorDescription = "Неизвестное описание" + } + + if (!reauthenticationLocation) { + reauthenticationLocation = '/'; + } + + return new ErrorOnPageModel(error, errorDescription, reauthenticationLocation); + } + ), + takeUntil(this.onDestroy) + ) + } + + goToAuth(url: string) { + this.windowService.get().location.href = url; + } + + ngOnDestroy(): void { + this.onDestroy.next(); + this.onDestroy.complete(); + } + + +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/generation-cookie-csrf.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/generation-cookie-csrf.service.spec.ts new file mode 100644 index 0000000..ecbff02 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/generation-cookie-csrf.service.spec.ts @@ -0,0 +1,51 @@ +import { TestBed } from '@angular/core/testing'; + +import { GenerationCookieCsrfService } from './generation-cookie-csrf.service'; +import { CookieService } from './cookie.service'; + +describe('GenerationCookieCsrfService', () => { + let service: GenerationCookieCsrfService; + let cookieServiceSpy: jasmine.SpyObj; + + beforeEach(() => { + + TestBed.configureTestingModule({ + providers: [ + GenerationCookieCsrfService, + { + provide: CookieService, useValue: jasmine.createSpyObj('CookieService', ['setCookie']) + } + ] + }); + service = TestBed.inject(GenerationCookieCsrfService); + cookieServiceSpy = TestBed.inject(CookieService) as jasmine.SpyObj; + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('generateCookieCsrf', () => { + // given (instead of when) + + const expectedCookieName = service.xsrfTokenCookieName; + + let actualCookieName: string = ''; + let actualCookieValue: string = ''; + cookieServiceSpy.setCookie.and.callFake( (name, value, options) => { + actualCookieName = name; + actualCookieValue = value; + }) + + // when + + service.generateCookieCsrf(); + + // then (instead of verify) + + expect(cookieServiceSpy.setCookie.calls.count()) + .toBe(1); + expect(actualCookieName).toEqual(expectedCookieName); + expect(actualCookieValue).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/generation-cookie-csrf.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/generation-cookie-csrf.service.ts new file mode 100644 index 0000000..9bb927c --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/generation-cookie-csrf.service.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@angular/core'; +import { CookieService } from './cookie.service'; + +@Injectable({ + providedIn: 'root' +}) +export class GenerationCookieCsrfService { + + readonly xsrfTokenCookieName = "XSRF-TOKEN"; + + constructor( + private cookieService: CookieService + ) { } + + generateCookieCsrf(){ + + //устанавливаем cookie от CSRF + this.cookieService.setCookie(this.xsrfTokenCookieName, this.generateRandomString(20), { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax' + }); + + } + + private generateRandomString(length: number): string { + + let result: string = ''; + let characters: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let charactersLength: number = characters.length; + + for (let i: number = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + + return result; + } + +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.component.html new file mode 100644 index 0000000..8f7a31e --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.component.html @@ -0,0 +1,68 @@ + +
+ logo + +

Login

+ +
+ +
+ +
+ + + Enter the login + + (Ctrl+L) Login to log in to the system + {{login.value.length || 0}} + + + The login cannot be empty + + + The minimum login consists of 4 characters + + + + + + + Enter the password + + + + (Ctrl+P) Password to log in to the system + {{password.value.length || 0}} + + + The password cannot be empty + + + The password is invalid + + + + + Remember me + + + +
+
+ + +
\ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.component.scss new file mode 100644 index 0000000..bfdca74 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.component.scss @@ -0,0 +1,77 @@ +:host { + min-width: inherit; + width: inherit; + min-height: inherit; + height: inherit; + display: flex; + justify-content: space-between; +} + + +form#login-form > mat-form-field { + margin-bottom: 1em; +} + +form#login-form > mat-checkbox { + margin-bottom: 1em; +} + + +#submitButton:hover i.fas.door::before { + content: "\f52b"; +} + +#submitButton i.fas.door::before { + content: "\f52a"; +} + +#submitButton[disabled] i.fas.door::before, +#submitButton[disabled]:hover i.fas.door::before { + content: "\f52a"; +} + +form#login-form{ + width: 30em; +} + +.left-part { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + padding: 2em; +} + +.right-part { + width: 80vw; + background-size: cover; + background-position: center; + background-image: url(/assets/stav.jpg); +} + +@media (max-width: 960px) { + .right-part { + display: none; + } + + .left-part { + width: 100%; + padding: 0; + } +} + +@media (max-width: 960px) { + .right-part { + display: none; + } + + .left-part { + width: 100%; + padding: 0; + } + + form#login-form{ + width: 22em; + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.component.spec.ts new file mode 100644 index 0000000..717b29b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.component.spec.ts @@ -0,0 +1,303 @@ +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; + +import { LoginComponent } from './login.component'; +import { DebugElement, Renderer2 } from '@angular/core'; +import { GenerationCookieCsrfService } from '../generation-cookie-csrf.service'; +import { LoginService } from './login.service'; +import { By } from '@angular/platform-browser'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { of, throwError } from 'rxjs'; +import { ResponseWithRedirectModel } from '../models/response-with-redirect.model'; +import { WindowService } from '../window.service'; + +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import { HarnessLoader } from '@angular/cdk/testing'; +import {MatCheckboxHarness} from '@angular/material/checkbox/testing'; +import { HttpErrorResponse } from '@angular/common/http'; + +describe('LoginComponent', () => { + let generationCookieCsrfServiceSpy: jasmine.SpyObj; + let loginServiceSpy: jasmine.SpyObj; + let windowServiceSpy: jasmine.SpyObj; + + let component: LoginComponent; + let fixture: ComponentFixture; + + let hostDe: DebugElement; + + let loginInput: HTMLInputElement; + let passwordInput: HTMLInputElement; + let checkbox:MatCheckboxHarness; + let submitButton: HTMLButtonElement; + + let hideOrShowPasswordButton: HTMLButtonElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LoginComponent, NoopAnimationsModule], + providers: [ + { + provide: Renderer2, + useValue: jasmine.createSpyObj('Renderer2', ['selectRootElement']) + }, + { + provide: GenerationCookieCsrfService, + useValue: jasmine.createSpyObj('GenerationCookieCsrfService', ['generateCookieCsrf']) + }, + { + provide: LoginService, + useValue: jasmine.createSpyObj('LoginService', ['login']) + }, + { + provide: WindowService, useValue: jasmine.createSpyObj('WindowService', ['get']) + } + ] + }) + .compileComponents() + .then(async () => { + generationCookieCsrfServiceSpy = TestBed.inject(GenerationCookieCsrfService) as jasmine.SpyObj; + loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj; + windowServiceSpy = TestBed.inject(WindowService) as jasmine.SpyObj; + + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + + let loader: HarnessLoader =TestbedHarnessEnvironment.loader(fixture) + + checkbox = await loader.getHarness(MatCheckboxHarness.with({ name: 'isRemember'})); + + hostDe = fixture.debugElement; + + loginInput = hostDe.query(By.css("#login")).nativeElement; + passwordInput = hostDe.query(By.css("#password")).nativeElement; + submitButton = hostDe.query(By.css("#submitButton")).nativeElement; + + hideOrShowPasswordButton = hostDe.query(By.css("button[matTooltip='Show Password']")).nativeElement; + + }); + + }); + + + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + + it('login should be succeed (GUI)', fakeAsync(() => { + // given (instead of when) + + const expectedLogin ="someLogin"; + const expectedPassword = "4Jof0#@V4cTaFev0"; + + const responseWithRedirectModel: ResponseWithRedirectModel = { + redirect_to: "/some_url" + }; + + loginServiceSpy.login.and.returnValue(of(responseWithRedirectModel)); + + const window = { + "location": { + "href": {} + } + } + + windowServiceSpy.get.and.returnValue(window); + + // check autofocus + component.ngAfterViewInit(); + tick(2); + fixture.detectChanges(); + const focusElement: HTMLElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(loginInput); + + // when + + loginInput.value = expectedLogin; + loginInput.dispatchEvent(new Event('input')); + + passwordInput.value = expectedPassword; + passwordInput.dispatchEvent(new Event('input')); + + checkbox.toggle(); + tick(); + + fixture.detectChanges(); + expect(component.isRemember.value).withContext("component.isRemember === true").toEqual(true); + + + // check hide/show password + hideOrShowPasswordButton.click(); + tick(); + expect(component.passwordShowFlag).withContext("passwordShowFlag === false").toEqual(true); + + hideOrShowPasswordButton.click(); + tick(); + expect(component.passwordShowFlag).withContext("passwordShowFlag === true").toEqual(false); + + // form valid? + expect(submitButton.disabled).withContext("submitButton.disabled === true").toEqual(false); + expect(component.loginFormGroup.invalid).withContext("component.loginFormGroup.invalid === true").toEqual(false); + + submitButton.click(); + tick(); + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(loginServiceSpy.login.calls.count()) + .withContext("loginServiceSpy.login.calls.count() !== 1") + .toBe(1); + expect(loginServiceSpy.login.calls.first().args) + .withContext(`loginServiceSpy.login.calls.first().args != [${expectedLogin} ${expectedPassword} ${component.isRemember.value} ]`) + .toEqual([expectedLogin, expectedPassword, component.isRemember.value]); + + expect(windowServiceSpy.get.calls.count()) + .withContext("windowServiceSpy.get.calls.count() !== 1") + .toBe(1); + })); + + + it('login should be fail (GUI)', fakeAsync(() => { + // given (instead of when) + + const expectedLogin ="someLogin"; + const expectedPassword = "4Jof0#@V4cTaFev0"; + + loginServiceSpy.login.and.returnValue(throwError(() => new HttpErrorResponse({status: 404}))); + + const window = { + "location": { + "href": {} + } + } + + windowServiceSpy.get.and.returnValue(window); + + // when + + loginInput.value = expectedLogin; + loginInput.dispatchEvent(new Event('input')); + + passwordInput.value = expectedPassword; + passwordInput.dispatchEvent(new Event('input')); + + checkbox.toggle(); + tick(); + + fixture.detectChanges(); + + // form valid? + + submitButton.click(); + tick(); + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(loginServiceSpy.login.calls.count()) + .withContext("loginServiceSpy.login.calls.count() !== 1") + .toBe(1); + expect(loginServiceSpy.login.calls.first().args) + .withContext(`loginServiceSpy.login.calls.first().args != [${expectedLogin} ${expectedPassword} ${component.isRemember.value} ]`) + .toEqual([expectedLogin, expectedPassword, component.isRemember.value]); + + })); + + it('test hot key', () => { + // given (instead of when) + + // when + + // login focus + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'l', + ctrlKey: true + })); + fixture.detectChanges(); + let focusElement: HTMLElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(loginInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'L', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(loginInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'д', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(loginInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'Д', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(loginInput); + + // password focus + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'p', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(passwordInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'P', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(passwordInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'З', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(passwordInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'з', + ctrlKey: true + })); + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'ж', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(passwordInput); + + // enter + window.dispatchEvent(new KeyboardEvent('keydown', { + key: 'enter' + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(passwordInput); + + // then (instead of verify) + + }); + + +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.component.ts new file mode 100644 index 0000000..d5c22a2 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.component.ts @@ -0,0 +1,168 @@ +import { Component, HostListener, OnDestroy, Renderer2 } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { LoginService } from './login.service'; +import { HttpClientModule, HttpErrorResponse } from '@angular/common/http'; +import { Subject, first, takeUntil } from 'rxjs'; +import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { MatTooltip, MatTooltipModule } from '@angular/material/tooltip'; +import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; + +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatButtonModule } from '@angular/material/button'; + +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { GenerationCookieCsrfService } from '../generation-cookie-csrf.service'; +import { WindowService } from '../window.service'; + +@Component({ + selector: 'app-login', + standalone: true, + imports: [ + CommonModule, + HttpClientModule, + ReactiveFormsModule, + MatProgressSpinnerModule, + MatFormFieldModule, + MatInputModule, + MatTooltipModule, + MatButtonModule, + MatCheckboxModule + ], + templateUrl: './login.component.html', + styleUrl: './login.component.scss' +}) +export class LoginComponent implements OnDestroy { + // to clear the rxjs memory + // для очистки памяти rxjs + private readonly onDestroy = new Subject(); + + readonly loginFormGroup = new FormGroup({ + login: new FormControl('', { + nonNullable: true, validators: [ + Validators.required, + Validators.minLength(4) + ] + }), + password: new FormControl('', { + nonNullable: true, validators: [ + Validators.required, + Validators.pattern('^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$') + ] + }), + isRemember: new FormControl(false, { nonNullable: true }), + }); + + get login() { return this.loginFormGroup.controls.login } + get password() { return this.loginFormGroup.controls.password } + get isRemember() { return this.loginFormGroup.controls.isRemember } + + // show or hide password + // показывать или скрывать пароль + passwordShowFlag: boolean = false; + + // waiting for a response from the server + // ожидание ответа от сервера + getResponseFromServerFlag: boolean = false; + + // show right side + // показать правую часть + isShowRightPart: boolean = true; + + @HostListener('window:keydown.enter', ['$event']) onKeydownEnterHandler(event: KeyboardEvent) { + this.submit(); + } + + @HostListener('window:keydown', ['$event']) onKeydownHandler(event: KeyboardEvent) { + if (!event.ctrlKey) { return; } + + if (event.key === 'l' || event.key === 'L' || event.key === 'д' || event.key === 'Д') { + this.focusLogin(); + return false; + } else if (event.key === 'p' || event.key === 'P' || event.key === 'З' || event.key === 'з') { + this.renderer.selectRootElement("#password")?.focus(); + return false; + } + + return; + } + + constructor( + private renderer: Renderer2, + private generationCookieCsrfService: GenerationCookieCsrfService, + private loginService: LoginService, + private breakpointObserver: BreakpointObserver, + private windowService: WindowService + ) { + + this.breakpointObserver + .observe(Breakpoints.Small) + .subscribe(result => this.isShowRightPart = result.matches ? false : true) + } + + + passwordDisplay(tooltip: MatTooltip) { + this.passwordShowFlag = !this.passwordShowFlag; + + if (this.passwordShowFlag) { + tooltip.message = 'Hide Password'; + } else { + tooltip.message = 'Show Password'; + } + tooltip.show(); + + } + + ngAfterViewInit() { + setTimeout(() => { + this.focusLogin(); + }, 1); + } + + focusLogin() { + this.renderer.selectRootElement("#login")?.focus(); + } + + submit() { + + if (this.loginFormGroup?.valid && !this.getResponseFromServerFlag) { + + this.getResponseFromServerFlag = true; + + this.generationCookieCsrfService.generateCookieCsrf(); + + this.loginService.login( + this.login.value, + this.password.value, + this.isRemember.value + ) + .pipe( + first(), + takeUntil(this.onDestroy) + ) + .subscribe({ + next: (responseLogin) => { + + this.windowService.get().location.href = responseLogin.redirect_to; + + // received response + // ответ получили + this.getResponseFromServerFlag = false; + }, + error: (error: HttpErrorResponse) => { + + // received response + // ответ получили + this.getResponseFromServerFlag = false; + } + }); + + } + } + + ngOnDestroy(): void { + this.onDestroy.next(); + this.onDestroy.complete(); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.service.spec.ts new file mode 100644 index 0000000..876efa8 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.service.spec.ts @@ -0,0 +1,119 @@ +import { TestBed } from '@angular/core/testing'; + +import { LoginService } from './login.service'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { environment } from '../../environments/environment'; +import { ResponseWithRedirectModel } from '../models/response-with-redirect.model'; +import { ErrorService } from '../error.service'; +import { HttpErrorResponse } from '@angular/common/http'; + +describe('LoginService', () => { + let service: LoginService; + let errorServiceSpy: jasmine.SpyObj; + let httpTestingController: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ HttpClientTestingModule ], + providers: [ + LoginService, + { + provide: ErrorService, useValue: jasmine.createSpyObj('ErrorService', ['handle']) + } + ] + }); + service = TestBed.inject(LoginService); + errorServiceSpy = TestBed.inject(ErrorService) as jasmine.SpyObj; + httpTestingController = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpTestingController.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('login should be success', (done: DoneFn) => { + // given (instead of when) + + const loginHttpURL = `${environment.apiUrl}/login`; + + const loginModel = { + "login": "some_login", + "password": "some_password", + "isRemember": true + }; + + const responseWithRedirectModel: ResponseWithRedirectModel = { + redirect_to: "some_redirect" + }; + + // when + + service.login(loginModel.login, loginModel.password, loginModel.isRemember) + .subscribe({ + next: responseWithRedirectModelLocal => { + expect(responseWithRedirectModelLocal).toEqual(responseWithRedirectModel); + done(); + }, + error: er => done.fail('responseWithRedirectModelLocal is expected') + }); + + const req = httpTestingController.expectOne(loginHttpURL); + req.flush(responseWithRedirectModel); + + // then (instead of verify) + + expect(req.request.method).toEqual('POST'); + expect(req.request.body).toEqual(loginModel); + }); + + it('login should be fail', done => { + // given (instead of when) + + const loginHttpURL = `${environment.apiUrl}/login`; + + const loginModel = { + "login": "some_login", + "password": "some_password", + "isRemember": true + }; + + const expectedErrorResponse = new HttpErrorResponse({ + error: '["password is invalid"]', + url: loginHttpURL, + status: 400, + statusText: 'Bad request' + }); + + // when + + service.login(loginModel.login, loginModel.password, loginModel.isRemember) + .subscribe({ + next: responseWithRedirectModelLocal => done.fail('login not expected'), + error: (actualErrorResponse: HttpErrorResponse) => { + expect(actualErrorResponse).toEqual(expectedErrorResponse); + done(); + } + }); + + + const req = httpTestingController.expectOne(loginHttpURL); + + req.flush(expectedErrorResponse.error, expectedErrorResponse); + + // then (instead of verify) + + expect(req.request.method).toEqual('POST'); + expect(req.request.body).toEqual(loginModel); + + expect(errorServiceSpy.handle.calls.count()) + .toBe(1); + expect(errorServiceSpy.handle.calls.first().args) + .toEqual([ expectedErrorResponse.error ]); + }); + +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.service.ts new file mode 100644 index 0000000..70a9388 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/login/login.service.ts @@ -0,0 +1,32 @@ +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { ErrorService } from '../error.service'; +import { Observable, catchError, throwError } from 'rxjs'; +import { ResponseWithRedirectModel } from '../models/response-with-redirect.model'; +import { environment } from '../../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class LoginService { + + constructor(private http: HttpClient, private errorService: ErrorService) { } + + login(login: string, password: string, isRemember: boolean): Observable { + return this.http.post( + `${environment.apiUrl}/login`, + { + "login": login, + "password": password, + "isRemember": isRemember + }) + .pipe( + catchError(this.errorHandler.bind(this)) + ); + } + + private errorHandler(error: HttpErrorResponse) { + this.errorService.handle(error.error) + return throwError(() => error) + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-handled/logout-handled.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-handled/logout-handled.component.html new file mode 100644 index 0000000..ba30a1e --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-handled/logout-handled.component.html @@ -0,0 +1,11 @@ + +logo + + +

You have successfully exited all applications

+

You have canceled logging out of all applications

+ +

To continue working, close the browser and open the required application again

+ + +logo \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-handled/logout-handled.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-handled/logout-handled.component.scss new file mode 100644 index 0000000..3b7d5c0 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-handled/logout-handled.component.scss @@ -0,0 +1,9 @@ +:host { + min-width: inherit; + width: inherit; + min-height: inherit; + height: inherit; + display: flex; + flex-direction: column; + align-items: center; +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-handled/logout-handled.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-handled/logout-handled.component.spec.ts new file mode 100644 index 0000000..5cedbf1 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-handled/logout-handled.component.spec.ts @@ -0,0 +1,192 @@ +import { TestBed } from '@angular/core/testing'; +import { provideRouter } from '@angular/router'; +import { WindowService } from '../window.service'; +import { LogoutHandledComponent } from './logout-handled.component'; + +import { RouterTestingHarness } from '@angular/router/testing'; +import { CookieService } from '../cookie.service'; + +describe('LogoutHandledComponent', () => { + + let harness: RouterTestingHarness; + + let windowServiceSpy: jasmine.SpyObj; + + let cookieServiceSpy: jasmine.SpyObj; + + beforeEach(async () => { + + await TestBed.configureTestingModule({ + imports: [LogoutHandledComponent], + providers: [ + provideRouter([ + { + path: "logout/:status", + component: LogoutHandledComponent + }, + ]), + { + provide: WindowService, useValue: jasmine.createSpyObj('WindowService', ['get']) + }, + { + provide: CookieService, useValue: jasmine.createSpyObj('CookieService', ['getCookie', 'deleteCookie']) + } + ] + }) + .compileComponents() + .then(async () => { + harness = await RouterTestingHarness.create(); + + windowServiceSpy = TestBed.inject(WindowService) as jasmine.SpyObj; + + cookieServiceSpy = TestBed.inject(CookieService) as jasmine.SpyObj; + }); + }); + + + it('logout/success', done => { + // given (instead of when) + + const windowObj = { + "history": { + "go": (amount: number) => { } + } + } + windowServiceSpy.get.and.returnValue(windowObj); + + cookieServiceSpy.getCookie.and.returnValue(undefined); + + // when + harness.navigateByUrl(`/logout/success`, LogoutHandledComponent) + .then(comp => { + expect(comp).toBeTruthy(); + + expect(cookieServiceSpy.getCookie.calls.count()) + .withContext("cookieServiceSpy.getCookie.calls.count() !== 1") + .toBe(1); + + expect(cookieServiceSpy.getCookie.calls.first().args) + .withContext(`cookieServiceSpy.getCookie.calls.first().args != ["logout" ]`) + .toEqual(["logout"]); + + comp.isConfirmed$ + .subscribe({ + next: flag => { + expect(flag).toEqual(true); + done(); + }, + error: (er) => done.fail("isConfirmed is incorrect") + }); + + }) + .catch(er => done.fail("isConfirmed is incorrect") + ) + + // then (instead of verify) + + }); + + + it('logout/ffff', done => { + // given (instead of when) + + const windowObj = { + "history": { + "go": (amount: number) => { } + } + } + windowServiceSpy.get.and.returnValue(windowObj); + + cookieServiceSpy.getCookie.and.returnValue("sss"); + + // when + harness.navigateByUrl(`/logout/ffff`, LogoutHandledComponent) + .then(comp => { + expect(comp).toBeTruthy(); + + expect(windowServiceSpy.get.calls.count()) + .withContext("windowServiceSpy.get.calls.count() !== 1") + .toBe(1); + + expect(cookieServiceSpy.getCookie.calls.count()) + .withContext("cookieServiceSpy.getCookie.calls.count() !== 1") + .toBe(1); + + expect(cookieServiceSpy.getCookie.calls.first().args) + .withContext(`cookieServiceSpy.getCookie.calls.first().args != ["logout" ]`) + .toEqual(["logout"]); + + comp.isConfirmed$ + .subscribe({ + next: flag => { + expect(flag).toEqual(false); + done(); + }, + error: (er) => done.fail("isConfirmed is incorrect") + }); + + }) + .catch(er => done.fail("isConfirmed is incorrect") + ) + + // then (instead of verify) + + }); + + + it('logout/cancel', done => { + // given (instead of when) + + const windowObj = { + "history": { + "go": (amount: number) => { } + } + } + windowServiceSpy.get.and.returnValue(windowObj); + + cookieServiceSpy.getCookie.and.returnValue("logout"); + + // when + harness.navigateByUrl(`/logout/cancel`, LogoutHandledComponent) + .then(comp => { + expect(comp).toBeTruthy(); + + expect(windowServiceSpy.get.calls.count()) + .withContext("windowServiceSpy.get.calls.count() !== 1") + .toBe(1); + + expect(cookieServiceSpy.getCookie.calls.count()) + .withContext("cookieServiceSpy.getCookie.calls.count() !== 1") + .toBe(1); + + expect(cookieServiceSpy.getCookie.calls.first().args) + .withContext(`cookieServiceSpy.getCookie.calls.first().args != ["logout" ]`) + .toEqual(["logout"]); + + expect(cookieServiceSpy.deleteCookie.calls.count()) + .withContext("cookieServiceSpy.deleteCookie.calls.count() !== 1") + .toBe(1); + + expect(cookieServiceSpy.deleteCookie.calls.first().args) + .withContext(`cookieServiceSpy.deleteCookie.calls.first().args != ["logout" ]`) + .toEqual(["logout"]); + + comp.isConfirmed$ + .subscribe({ + next: flag => { + expect(flag).toEqual(false); + done(); + }, + error: (er) => done.fail("isConfirmed is incorrect") + }); + + }) + .catch(er => done.fail("isConfirmed is incorrect") + ) + + // then (instead of verify) + + }); + + +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-handled/logout-handled.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-handled/logout-handled.component.ts new file mode 100644 index 0000000..cacd3b5 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-handled/logout-handled.component.ts @@ -0,0 +1,49 @@ +import { Component, OnDestroy } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ActivatedRoute, Params } from '@angular/router'; +import { Observable, Subject, first, map, takeUntil } from 'rxjs'; +import { WindowService } from '../window.service'; +import { CookieService } from '../cookie.service'; + +@Component({ + selector: 'app-logout-handled', + standalone: true, + imports: [CommonModule], + templateUrl: './logout-handled.component.html', + styleUrl: './logout-handled.component.scss' +}) +export class LogoutHandledComponent implements OnDestroy { + + // to clear the rxjs memory + // для очистки памяти rxjs + private readonly onDestroy = new Subject(); + + isConfirmed$: Observable; + + constructor(private route: ActivatedRoute, private windowService: WindowService, private cookieService: CookieService) { + + this.isConfirmed$ = this.route.params + .pipe( + first(), + map((params: Params) => + params["status"] === 'success' + ), + map((flag: boolean) => { + if(this.cookieService.getCookie("logout") === "logout"){ + this.cookieService.deleteCookie("logout"); + this.windowService.get().history.go(-2); + } else { + this.windowService.get().history.go(-1); + } + return flag; + }), + takeUntil(this.onDestroy) + ); + + } + + ngOnDestroy(): void { + this.onDestroy.next(); + this.onDestroy.complete(); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-request/logout-request.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-request/logout-request.component.html new file mode 100644 index 0000000..92ce9c8 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-request/logout-request.component.html @@ -0,0 +1,24 @@ + +logo + + +
+ +
+ + +

+ Are you sure you want to quit all applications? +

+ +
+ + +
+
\ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-request/logout-request.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-request/logout-request.component.scss new file mode 100644 index 0000000..fdc3abb --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-request/logout-request.component.scss @@ -0,0 +1,13 @@ +:host { + min-width: inherit; + width: inherit; + min-height: inherit; + height: inherit; + display: flex; + flex-direction: column; + align-items: center; +} + +button { + margin: 1em; +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-request/logout-request.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-request/logout-request.component.spec.ts new file mode 100644 index 0000000..c52577d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-request/logout-request.component.spec.ts @@ -0,0 +1,282 @@ +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; + +import { LogoutRequestComponent } from './logout-request.component'; +import { GenerationCookieCsrfService } from '../generation-cookie-csrf.service'; +import { LogoutService } from '../logout.service'; +import { WindowService } from '../window.service'; +import { DebugElement } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { ResponseWithRedirectModel } from '../models/response-with-redirect.model'; +import { of, throwError } from 'rxjs'; +import { HttpErrorResponse } from '@angular/common/http'; +import { CookieService } from '../cookie.service'; + +describe('LogoutRequestComponent', () => { + let generationCookieCsrfServiceSpy: jasmine.SpyObj; + let logoutServiceSpy: jasmine.SpyObj; + let windowServiceSpy: jasmine.SpyObj; + let cookieServiceSpy: jasmine.SpyObj; + + let component: LogoutRequestComponent; + let fixture: ComponentFixture; + + let hostDe: DebugElement; + + let yesButton: HTMLButtonElement; + let noButton: HTMLButtonElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LogoutRequestComponent], + providers: [ + { + provide: GenerationCookieCsrfService, + useValue: jasmine.createSpyObj('GenerationCookieCsrfService', ['generateCookieCsrf']) + }, + { + provide: LogoutService, + useValue: jasmine.createSpyObj('LogoutService', ['logoutSendResponse']) + }, + { + provide: WindowService, useValue: jasmine.createSpyObj('WindowService', ['get']) + }, + { + provide: CookieService, useValue: jasmine.createSpyObj('CookieService', ['setCookie']) + } + ] + }) + .compileComponents() + .then(async () => { + generationCookieCsrfServiceSpy = TestBed.inject(GenerationCookieCsrfService) as jasmine.SpyObj; + logoutServiceSpy = TestBed.inject(LogoutService) as jasmine.SpyObj; + windowServiceSpy = TestBed.inject(WindowService) as jasmine.SpyObj; + cookieServiceSpy = TestBed.inject(CookieService) as jasmine.SpyObj; + + fixture = TestBed.createComponent(LogoutRequestComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + + hostDe = fixture.debugElement; + + yesButton = hostDe.query(By.css("#yesButton")).nativeElement; + noButton = hostDe.query(By.css("#noButton")).nativeElement; + + }); + + }); + + + it('yes (GUI)', fakeAsync(() => { + // given (instead of when) + + const responseWithRedirectModel: ResponseWithRedirectModel = { + redirect_to: "/some_url" + }; + logoutServiceSpy.logoutSendResponse.and.returnValue(of(responseWithRedirectModel)); + + const windowObj = { + "location": { + "href": {} + } + } + windowServiceSpy.get.and.returnValue(windowObj); + // when + + yesButton.click(); + tick(); + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(logoutServiceSpy.logoutSendResponse.calls.count()) + .withContext("consentServiceSpy.consentSuccess.calls.count() !== 1") + .toBe(1); + expect(logoutServiceSpy.logoutSendResponse.calls.first().args) + .withContext(`consentServiceSpy.consentSuccess.calls.first().args != [${true} ]`) + .toEqual([true]); + + expect(windowServiceSpy.get.calls.count()) + .withContext("windowServiceSpy.get.calls.count() !== 1") + .toBe(1); + + expect(cookieServiceSpy.setCookie.calls.count()) + .withContext("cookieServiceSpy.setCookie.calls.count() !== 1") + .toBe(1); + expect(cookieServiceSpy.setCookie.calls.first().args) + .withContext(`cookieServiceSpy.setCookie.calls.first().args != ["logout", "logout", { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax' + } ]`) + .toEqual(["logout", "logout", { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax' + }]); + + + })); + + + it('(enter)', fakeAsync(() => { + // given (instead of when) + + const responseWithRedirectModel: ResponseWithRedirectModel = { + redirect_to: "/some_url" + }; + logoutServiceSpy.logoutSendResponse.and.returnValue(of(responseWithRedirectModel)); + + const windowObj = { + "location": { + "href": {} + } + } + windowServiceSpy.get.and.returnValue(windowObj); + + // when + + // enter + window.dispatchEvent(new KeyboardEvent('keydown', { + key: 'enter', + })); + fixture.detectChanges(); + + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(logoutServiceSpy.logoutSendResponse.calls.count()) + .withContext("consentServiceSpy.consentSuccess.calls.count() !== 1") + .toBe(1); + expect(logoutServiceSpy.logoutSendResponse.calls.first().args) + .withContext(`consentServiceSpy.consentSuccess.calls.first().args != [${true} ]`) + .toEqual([true]); + + expect(windowServiceSpy.get.calls.count()) + .withContext("windowServiceSpy.get.calls.count() !== 1") + .toBe(1); + + expect(cookieServiceSpy.setCookie.calls.count()) + .withContext("cookieServiceSpy.setCookie.calls.count() !== 1") + .toBe(1); + expect(cookieServiceSpy.setCookie.calls.first().args) + .withContext(`cookieServiceSpy.setCookie.calls.first().args != ["logout", "logout", { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax' + } ]`) + .toEqual(["logout", "logout", { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax' + }]); + })); + + + + it('(esc)', fakeAsync(() => { + // given (instead of when) + + const responseWithRedirectModel: ResponseWithRedirectModel = { + redirect_to: "/some_url" + }; + logoutServiceSpy.logoutSendResponse.and.returnValue(of(responseWithRedirectModel)); + + const windowObj = { + "location": { + "href": {} + } + } + windowServiceSpy.get.and.returnValue(windowObj); + + // when + + // enter + window.dispatchEvent(new KeyboardEvent('keydown', { + key: 'Escape', + })); + fixture.detectChanges(); + + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(logoutServiceSpy.logoutSendResponse.calls.count()) + .withContext("consentServiceSpy.consentSuccess.calls.count() !== 1") + .toBe(1); + expect(logoutServiceSpy.logoutSendResponse.calls.first().args) + .withContext(`consentServiceSpy.consentSuccess.calls.first().args != [${false} ]`) + .toEqual([false]); + + expect(windowServiceSpy.get.calls.count()) + .withContext("windowServiceSpy.get.calls.count() !== 1") + .toBe(1); + + expect(cookieServiceSpy.setCookie.calls.count()) + .withContext("cookieServiceSpy.setCookie.calls.count() !== 1") + .toBe(1); + expect(cookieServiceSpy.setCookie.calls.first().args) + .withContext(`cookieServiceSpy.setCookie.calls.first().args != ["logout", "logout", { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax' + } ]`) + .toEqual(["logout", "logout", { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax' + }]); + })); + + + it('yse (GUI) (fail)', fakeAsync(() => { + // given (instead of when) + + logoutServiceSpy.logoutSendResponse.and.returnValue(throwError(() => new HttpErrorResponse({ status: 404 }))); + // when + + yesButton.click(); + tick(); + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(logoutServiceSpy.logoutSendResponse.calls.count()) + .withContext("consentServiceSpy.consentSuccess.calls.count() !== 1") + .toBe(1); + + expect(cookieServiceSpy.setCookie.calls.count()) + .withContext("cookieServiceSpy.setCookie.calls.count() !== 1") + .toBe(1); + expect(cookieServiceSpy.setCookie.calls.first().args) + .withContext(`cookieServiceSpy.setCookie.calls.first().args != ["logout", "logout", { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax' + } ]`) + .toEqual(["logout", "logout", { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax' + }]); + + })); + + +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-request/logout-request.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-request/logout-request.component.ts new file mode 100644 index 0000000..9ca18e3 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout-request/logout-request.component.ts @@ -0,0 +1,92 @@ +import { Component, HostListener, OnDestroy } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { Subject, first, takeUntil } from 'rxjs'; +import { LogoutService } from '../logout.service'; +import { GenerationCookieCsrfService } from '../generation-cookie-csrf.service'; +import { WindowService } from '../window.service'; +import { HttpErrorResponse } from '@angular/common/http'; +import { MatButtonModule } from '@angular/material/button'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { CookieService } from '../cookie.service'; + +@Component({ + selector: 'app-logout-request', + standalone: true, + imports: [ + CommonModule, + MatProgressSpinnerModule, + MatButtonModule, + MatTooltipModule + ], + templateUrl: './logout-request.component.html', + styleUrl: './logout-request.component.scss' +}) +export class LogoutRequestComponent implements OnDestroy { + + // to clear the rxjs memory + // для очистки памяти rxjs + private readonly onDestroy = new Subject(); + + // waiting for a response from the server + // ожидание ответа от сервера + getResponseFromServerFlag: boolean = false; + + @HostListener('window:keydown.enter', ['$event']) onKeydownEnterHandler(event: KeyboardEvent) { + this.isConfirmedOrNot(true); + } + + @HostListener('window:keydown.esc', ['$event']) onKeydownEnterHandler2(event: KeyboardEvent) { + this.isConfirmedOrNot(false); + } + + constructor(private logoutService: LogoutService, + private generationCookieCsrfService: GenerationCookieCsrfService, + private windowService: WindowService, + private cookieService: CookieService) { } + + isConfirmedOrNot(isConfirmed: boolean) { + + if (!this.getResponseFromServerFlag) { + + this.getResponseFromServerFlag = true; + + this.generationCookieCsrfService.generateCookieCsrf(); + + this.cookieService.setCookie("logout", "logout", { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax' + }); + + this.logoutService.logoutSendResponse(isConfirmed) + .pipe( + first(), + takeUntil(this.onDestroy) + ) + .subscribe({ + next: (responseLogin) => { + + this.windowService.get().location.href = responseLogin.redirect_to; + + // received response + // ответ получили + this.getResponseFromServerFlag = false; + }, + error: (error: HttpErrorResponse) => { + + // received response + // ответ получили + this.getResponseFromServerFlag = false; + } + }); + } + + } + + ngOnDestroy(): void { + this.onDestroy.next(); + this.onDestroy.complete(); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout.service.spec.ts new file mode 100644 index 0000000..d1a5126 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout.service.spec.ts @@ -0,0 +1,118 @@ +import { TestBed } from '@angular/core/testing'; + +import { LogoutService } from './logout.service'; +import { ErrorService } from './error.service'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { environment } from '../environments/environment'; +import { ResponseWithRedirectModel } from './models/response-with-redirect.model'; +import { HttpErrorResponse } from '@angular/common/http'; + +describe('LogoutService', () => { + let service: LogoutService; + let errorServiceSpy: jasmine.SpyObj; + let httpTestingController: HttpTestingController; + + beforeEach(() => { + + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + LogoutService, + { + provide: ErrorService, useValue: jasmine.createSpyObj('ErrorService', ['handle']) + } + ] + }); + service = TestBed.inject(LogoutService); + errorServiceSpy = TestBed.inject(ErrorService) as jasmine.SpyObj; + httpTestingController = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpTestingController.verify(); + }); + + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('logoutSendResponse - confirm', done => { + // given (instead of when) + + const logoutSendResponseHttpURL = `${environment.apiUrl}/logout/`; + + const responseWithRedirectModel: ResponseWithRedirectModel = { + redirect_to: "/some_url" + }; + + const logoutSendResponseModel = { + "isConfirmed": true + }; + + // when + + service.logoutSendResponse(logoutSendResponseModel.isConfirmed) + .subscribe({ + next: responseWithRedirectModelLocal => { + expect(responseWithRedirectModelLocal).toEqual(responseWithRedirectModel); + done(); + }, + error: er => done.fail('responseWithRedirectModelLocal is expected') + }); + + const req = httpTestingController.expectOne(logoutSendResponseHttpURL); + req.flush(responseWithRedirectModel); + + // then (instead of verify) + + expect(req.request.method).toEqual('PUT'); + expect(req.request.body).toEqual(logoutSendResponseModel); + + }); + + it('logoutSendResponse should be fail', done => { + // given (instead of when) + + const logoutSendResponseHttpURL = `${environment.apiUrl}/logout/`; + + const expectedErrorResponse = new HttpErrorResponse({ + error: '["password is invalid"]', + url: logoutSendResponseHttpURL, + status: 400, + statusText: 'Bad request' + }); + + const logoutSendResponseModel = { + "isConfirmed": true + }; + + // when + + service.logoutSendResponse(logoutSendResponseModel.isConfirmed) + .subscribe({ + next: responseWithRedirectModelLocal => done.fail('data not expected'), + error: (actualErrorResponse: HttpErrorResponse) => { + expect(actualErrorResponse).toEqual(expectedErrorResponse); + done(); + } + }); + + + const req = httpTestingController.expectOne(logoutSendResponseHttpURL); + + req.flush(expectedErrorResponse.error, expectedErrorResponse); + + // then (instead of verify) + + expect(req.request.method).toEqual('PUT'); + expect(req.request.body).toEqual(logoutSendResponseModel); + + expect(errorServiceSpy.handle.calls.count()) + .toBe(1); + expect(errorServiceSpy.handle.calls.first().args) + .toEqual([ expectedErrorResponse.error ]); + }); + +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout.service.ts new file mode 100644 index 0000000..5ae7f72 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/logout.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { ResponseWithRedirectModel } from './models/response-with-redirect.model'; +import { Observable, catchError, throwError } from 'rxjs'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { ErrorService } from './error.service'; +import { environment } from '../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class LogoutService { + + constructor(private http: HttpClient, private errorService: ErrorService) { } + + logoutSendResponse(isConfirmed: boolean): Observable { + return this.http.put( + `${environment.apiUrl}/logout/`, { + "isConfirmed": isConfirmed + }) + .pipe( + catchError(this.errorHandler.bind(this)) + ); + } + + private errorHandler(error: HttpErrorResponse) { + this.errorService.handle(error.error) + return throwError(() => error) + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/models/error-on-page.model.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/models/error-on-page.model.ts new file mode 100644 index 0000000..9e80220 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/models/error-on-page.model.ts @@ -0,0 +1,51 @@ +import { ErrorResponseCode } from "./error-response-code.model"; + +export class ErrorOnPageModel{ + error: string; + errorDescription: string; + reauthenticationLocation: string; + + constructor(error: string, errorDescription: string, reauthenticationLocation: string){ + this.error = error; + this.errorDescription = errorDescription; + this.reauthenticationLocation = reauthenticationLocation; + } + + + public static getErrorResponseCodeRus(errorResponseCodeStr: string): string { + let errorResponseCode: ErrorResponseCode = errorResponseCodeStr as ErrorResponseCode; + switch (errorResponseCode) { + case ErrorResponseCode.InvalidRequest: + return "Ошибка параметров запроса (invalid_request)"; + case ErrorResponseCode.UnauthorizedClient: + return "Клиент не может быть авторизован используя данный метод (unauthorized_client)"; + case ErrorResponseCode.AccessDenied: + return "Владелец ресурса или сервер авторизации отклонили запрос (access_denied)"; + case ErrorResponseCode.UnsupportedResponseType: + return "Сервер авторизации не поддерживает получение кода авторизации с помощью этого метода (unsupported_response_type)"; + case ErrorResponseCode.InvalidScope: + return "Запрошенные scopes недопустимы, неизвестны или имеют неправильную форму (invalid_scope)"; + case ErrorResponseCode.ServerError: + return "Сервер авторизации столкнулся с неожиданным условием, которое помешало ему выполнить запрос (server_error)"; + case ErrorResponseCode.TemporarilyUnavailable: + return "Сервер авторизации в настоящее время не может обработать запрос из-за временной перегрузки или технического обслуживания сервера (temporarily_unavailable)"; + case ErrorResponseCode.InsufficientScope: + return "Запрос требует более высоких привилегий, чем те, которые предоставляются токеном доступа (insufficient_scope)"; + case ErrorResponseCode.InvalidClient: + return "Ошибка проверки подлинности клиента (например, неизвестный клиент, проверка подлинности клиента не включена или неподдерживаемый метод проверки подлинности) (invalid_client)"; + case ErrorResponseCode.InvalidGrant: + return "Предоставленное разрешение на авторизацию (например, код авторизации, учетные данные владельца ресурса) или токен обновления недействительны, истек срок действия, отозваны, не соответствуют URI перенаправления, использованному в запросе на авторизацию, или были выданы другому клиенту. (invalid_grant)"; + case ErrorResponseCode.InvalidRedirectUri: + return "Значение одного или нескольких URI перенаправления недопустимо (invalid_redirect_uri)"; + case ErrorResponseCode.InvalidToken: + return "Предоставленный маркер доступа истек, отозван, имеет неправильную форму или недействителен по другим причинам (invalid_token)"; + case ErrorResponseCode.UnsupportedGrantType: + return "Тип предоставления авторизации не поддерживается сервером авторизации (unsupported_grant_type)"; + case ErrorResponseCode.UnsupportedTokenType: + return "Сервер авторизации не поддерживает отзыв представленного типа токена (unsupported_token_type)"; + default: + return `Ошибка (${errorResponseCodeStr})`; + } + } + +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/models/error-response-code.model.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/models/error-response-code.model.ts new file mode 100644 index 0000000..182aec1 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/models/error-response-code.model.ts @@ -0,0 +1,61 @@ +export const enum ErrorResponseCode { + + // invalid_request + // The request is missing a required parameter, includes an + // invalid parameter value, includes a parameter more than + // once, or is otherwise malformed. + InvalidRequest = "invalid_request", + + // unauthorized_client + // The client is not authorized to request an authorization + // code using this method. + UnauthorizedClient = "unauthorized_client", + + // access_denied + // The resource owner or authorization server denied the + // request. + AccessDenied = "access_denied", + + // unsupported_response_type + // The authorization server does not support obtaining an + // authorization code using this method. + UnsupportedResponseType = "unsupported_response_type", + + // invalid_scope + // The requested scope is invalid, unknown, or malformed. + InvalidScope = "invalid_scope", + + // server_error + // The authorization server encountered an unexpected + // condition that prevented it from fulfilling the request. + // (This error code is needed because a 500 Internal Server + // Error HTTP status code cannot be returned to the client + // via an HTTP redirect.) + ServerError = "server_error", + + // temporarily_unavailable + // The authorization server is currently unable to handle + // the request due to a temporary overloading or maintenance + // of the server. (This error code is needed because a 503 + // Service Unavailable HTTP status code cannot be returned + // to the client via an HTTP redirect.) + TemporarilyUnavailable = "temporarily_unavailable", + // insufficient_scope - + // The request requires higher privileges than provided by the access token. + InsufficientScope = "insufficient_scope", + // invalid_client - Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method) + InvalidClient = "invalid_client", + // invalid_grant - The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, + // revoked, does not match the redirection URI used in the authorization request, or was issued to another client. + InvalidGrant = "invalid_grant", + // invalid_redirect_uri - The value of one or more redirection URIs is invalid. + InvalidRedirectUri = "invalid_redirect_uri", + // invalid_token - The access token provided is expired, revoked, malformed, or invalid for other reasons + InvalidToken = "invalid_token", + //unsupported_grant_type - The authorization grant type is not supported by the authorization server. + UnsupportedGrantType = "unsupported_grant_type", + // unsupported_token_type - The authorization server does not support the revocation of the presented token type + UnsupportedTokenType = "unsupported_token_type" + + + } \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/models/response-with-redirect.model.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/models/response-with-redirect.model.ts new file mode 100644 index 0000000..138d177 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/models/response-with-redirect.model.ts @@ -0,0 +1,3 @@ +export class ResponseWithRedirectModel{ + redirect_to!:string; +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/not-found/not-found.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/not-found/not-found.component.html new file mode 100644 index 0000000..b4375ad --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/not-found/not-found.component.html @@ -0,0 +1,12 @@ +
+

404

+
Page not found
+ cry +
+ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/not-found/not-found.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/not-found/not-found.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/not-found/not-found.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/not-found/not-found.component.spec.ts new file mode 100644 index 0000000..0cd8d8b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/not-found/not-found.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NotFoundComponent } from './not-found.component'; + +describe('NotFoundComponent', () => { + let component: NotFoundComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [NotFoundComponent] + }); + fixture = TestBed.createComponent(NotFoundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/not-found/not-found.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/not-found/not-found.component.ts new file mode 100644 index 0000000..65054c8 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/not-found/not-found.component.ts @@ -0,0 +1,8 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-not-found', + templateUrl: './not-found.component.html', + styleUrls: ['./not-found.component.scss'] +}) +export class NotFoundComponent {} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration-routing.module.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration-routing.module.ts new file mode 100644 index 0000000..caf84a4 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration-routing.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { RegistrationComponent } from './registration.component'; + +const routes: Routes = [{ path: '', component: RegistrationComponent }]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class RegistrationRoutingModule { } diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.component.html new file mode 100644 index 0000000..3a3d95e --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.component.html @@ -0,0 +1,110 @@ +logo + +

Registration

+ +
+ +
+ +
+ + + Enter the login + + (Ctrl+L) Login to log in to the system + {{login.value.length || 0}} + + + The login cannot be empty + + + The minimum login consists of 4 characters + + + + + + + Enter the password + + + + (Ctrl+P) Password to log in to the system + {{password.value.length || 0}} + + + The password cannot be empty + + + The password is invalid + + + + + + Repeat password + + + + (Ctrl+R) Repeat password + {{repeatPassword.value.length || 0}} + + + The repeat password cannot be empty + + + + + + Enter the organization name + + (Ctrl+O) Organization name + {{orgName.value.length || 0}} + + + The organization name cannot be empty + + + The minimum organization name consists of 4 characters + + + + + + +
+ Password mismatch +
+
\ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.component.scss new file mode 100644 index 0000000..10d2073 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.component.scss @@ -0,0 +1,20 @@ +:host { + min-width: inherit; + width: inherit; + min-height: inherit; + height: inherit; + + display: flex; + flex-direction: column; + align-items: center; +} + +form#registration > mat-form-field { + margin-bottom: 1em; +} + +.er{ + color: #f44336; + text-align: center; + margin: 1em; +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.component.spec.ts new file mode 100644 index 0000000..b3e9eb6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.component.spec.ts @@ -0,0 +1,467 @@ +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { DebugElement, Renderer2 } from '@angular/core'; + +import { RegistrationComponent } from './registration.component'; +import { GenerationCookieCsrfService } from '../generation-cookie-csrf.service'; +import { RegistrationService } from './registration.service'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { of, throwError } from 'rxjs'; +import { By } from '@angular/platform-browser'; +import { HttpErrorResponse } from '@angular/common/http'; + +describe('RegistrationComponent', () => { + let renderer2Spy: jasmine.SpyObj; + let generationCookieCsrfServiceSpy: jasmine.SpyObj; + let registrationServiceSpy: jasmine.SpyObj; + + let component: RegistrationComponent; + let fixture: ComponentFixture; + + let hostDe: DebugElement; + + let loginInput: HTMLInputElement; + let passwordInput: HTMLInputElement; + let repeatPasswordInput: HTMLInputElement; + let orgNameInput: HTMLInputElement; + let submitButton: HTMLButtonElement; + + let hideOrShowPasswordButton1: HTMLButtonElement; + let hideOrShowPasswordButton2: HTMLButtonElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatFormFieldModule, + MatInputModule, + MatTooltipModule, + ReactiveFormsModule, + MatButtonModule, + MatProgressSpinnerModule, + BrowserAnimationsModule + ], + declarations: [RegistrationComponent], + providers: [ + { + provide: Renderer2, + useValue: jasmine.createSpyObj('Renderer2', ['selectRootElement']) + }, + { + provide: GenerationCookieCsrfService, + useValue: jasmine.createSpyObj('GenerationCookieCsrfService', ['generateCookieCsrf']) + }, + { + provide: RegistrationService, + useValue: jasmine.createSpyObj('RegistrationService', ['registration']) + } + ] + }).compileComponents(); + }); + + + beforeEach(() => { + renderer2Spy = TestBed.inject(Renderer2) as jasmine.SpyObj; + generationCookieCsrfServiceSpy = TestBed.inject(GenerationCookieCsrfService) as jasmine.SpyObj; + registrationServiceSpy = TestBed.inject(RegistrationService) as jasmine.SpyObj; + + fixture = TestBed.createComponent(RegistrationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + + hostDe = fixture.debugElement; + + loginInput = hostDe.query(By.css("#login")).nativeElement; + passwordInput = hostDe.query(By.css("#password")).nativeElement; + repeatPasswordInput = hostDe.query(By.css("#repeatPassword")).nativeElement; + orgNameInput = hostDe.query(By.css("#orgName")).nativeElement; + submitButton = hostDe.query(By.css("#submitButton")).nativeElement; + + hideOrShowPasswordButton1 = hostDe.query(By.css("#hideOrShowPasswordButton1")).nativeElement; + hideOrShowPasswordButton2 = hostDe.query(By.css("#hideOrShowPasswordButton2")).nativeElement; + }); + + it('should create', () => { + expect(component).toBeDefined(); + }); + + it('registration should be succeed', fakeAsync(() => { + // given (instead of when) + + const expectedLogin ="someLogin"; + const expectedPassword = "4Jof0#@V4cTaFev0"; + const expectedOrgName = "someOrg"; + + registrationServiceSpy.registration.and.returnValue(of(1)); + + // check autofocus + component.ngAfterViewInit(); + tick(2); + fixture.detectChanges(); + const focusElement: HTMLElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(loginInput); + + // when + + loginInput.value = expectedLogin; + loginInput.dispatchEvent(new Event('input')); + + passwordInput.value = expectedPassword; + passwordInput.dispatchEvent(new Event('input')); + + repeatPasswordInput.value = expectedPassword; + repeatPasswordInput.dispatchEvent(new Event('input')); + + orgNameInput.value = expectedOrgName; + orgNameInput.dispatchEvent(new Event('input')); + + fixture.detectChanges(); + + // check hide/show password + hideOrShowPasswordButton1.click(); + tick(); + expect(component.passwordShowFlag).withContext("passwordShowFlag1 === false").toEqual(true); + + hideOrShowPasswordButton1.click(); + tick(); + expect(component.passwordShowFlag).withContext("passwordShowFlag1 === true").toEqual(false); + + hideOrShowPasswordButton2.click(); + tick(); + expect(component.passwordShowFlag2).withContext("passwordShowFlag2 === false").toEqual(true); + + hideOrShowPasswordButton2.click(); + tick(); + expect(component.passwordShowFlag2).withContext("passwordShowFlag2 === true").toEqual(false); + + // form valid? + expect(submitButton.disabled).withContext("submitButton.disabled === true").toEqual(false); + expect(component.registrationFormGroup.invalid).withContext("submitButton.disabled === true").toEqual(false); + + submitButton.click(); + tick(10000); + + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(registrationServiceSpy.registration.calls.count()) + .withContext("registrationServiceSpy.registration.calls.count() !== 1") + .toBe(1); + expect(registrationServiceSpy.registration.calls.first().args) + .withContext(`registrationServiceSpy.registration.calls.first().args != [${expectedLogin} ${expectedPassword} ${expectedOrgName}]`) + .toEqual([expectedLogin, expectedPassword, expectedOrgName]); + + })); + + it('registration should be fail (enter)', fakeAsync(() => { + // given (instead of when) + + const expectedLogin ="someLogin"; + const expectedPassword = "4Jof0#@V4cTaFev0"; + const expectedOrgName = "someOrg"; + + registrationServiceSpy.registration.and.returnValue(throwError(() => new HttpErrorResponse({status: 404}))); + + // when + + loginInput.value = expectedLogin; + loginInput.dispatchEvent(new Event('input')); + + passwordInput.value = expectedPassword; + passwordInput.dispatchEvent(new Event('input')); + + repeatPasswordInput.value = expectedPassword; + repeatPasswordInput.dispatchEvent(new Event('input')); + + orgNameInput.value = expectedOrgName; + orgNameInput.dispatchEvent(new Event('input')); + + fixture.detectChanges(); + + // form valid? + expect(submitButton.disabled).withContext("submitButton.disabled === true").toEqual(false); + expect(component.registrationFormGroup.invalid).withContext("submitButton.disabled === true").toEqual(false); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'enter' + })); + + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(registrationServiceSpy.registration.calls.count()) + .withContext("registrationServiceSpy.registration.calls.count() !== 1") + .toBe(1); + expect(registrationServiceSpy.registration.calls.first().args) + .withContext(`registrationServiceSpy.registration.calls.first().args != [${expectedLogin} ${expectedPassword} ${expectedOrgName}]`) + .toEqual([expectedLogin, expectedPassword, expectedOrgName]); + + })); + + it('registration should be succeed (enter)', () => { + // given (instead of when) + + // when + + // login focus + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'l', + ctrlKey: true + })); + fixture.detectChanges(); + let focusElement: HTMLElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(loginInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'L', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(loginInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'д', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(loginInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'Д', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(loginInput); + + // password focus + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'p', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(passwordInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'P', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(passwordInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'З', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(passwordInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'з', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(passwordInput); + + // repeatPassword focus + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'r', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(repeatPasswordInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'R', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(repeatPasswordInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'К', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(repeatPasswordInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'к', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(repeatPasswordInput); + + // orgName focus + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'o', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(orgNameInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'O', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(orgNameInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'щ', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(orgNameInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'Щ', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(orgNameInput); + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'b', + ctrlKey: true + })); + fixture.detectChanges(); + focusElement = hostDe.query(By.css(":focus")).nativeElement; + expect(focusElement).toBe(orgNameInput); + + + // then (instead of verify) + + }); + + + it('login invalid', () => { + // given (instead of when) + + const expectedLogin ="som"; + + // when + + loginInput.value = expectedLogin; + loginInput.dispatchEvent(new Event('input')); + window.dispatchEvent(new KeyboardEvent('keypress',{ + key: 'tab' + })); + + fixture.detectChanges(); + + expect(component.login.invalid && (component.login.dirty || component.login.touched) && component.login.errors?.['minlength']).toBeTruthy(); + expect(submitButton.disabled).withContext("submitButton.disabled === false").toEqual(true); + expect(component.registrationFormGroup.invalid).withContext("submitButton.disabled === false").toEqual(true); + + // then (instead of verify) + + + }); + + it('password invalid', () => { + // given (instead of when) + + const expectedPassword = "zcvs"; + + // when + + passwordInput.value = expectedPassword; + passwordInput.dispatchEvent(new Event('input')); + window.dispatchEvent(new KeyboardEvent('keypress',{ + key: 'tab' + })); + + fixture.detectChanges(); + + expect(component.password.invalid && (component.password.dirty || component.password.touched) && component.password.errors?.['pattern']).toBeTruthy(); + expect(submitButton.disabled).withContext("submitButton.disabled === false").toEqual(true); + expect(component.registrationFormGroup.invalid).withContext("submitButton.disabled === false").toEqual(true); + + // then (instead of verify) + + + }); + + it('orgName invalid', () => { + // given (instead of when) + + const expectedOrgName = "xzv"; + + // when + + orgNameInput.value = expectedOrgName; + orgNameInput.dispatchEvent(new Event('input')); + window.dispatchEvent(new KeyboardEvent('keypress',{ + key: 'tab' + })); + + fixture.detectChanges(); + + expect(component.orgName.invalid && (component.orgName.dirty || component.orgName.touched) && component.orgName.errors?.['minlength']).toBeTruthy(); + expect(submitButton.disabled).withContext("submitButton.disabled === false").toEqual(true); + expect(component.registrationFormGroup.invalid).withContext("submitButton.disabled === false").toEqual(true); + + // then (instead of verify) + + + }); + + it('repeatPassword invalid', () => { + // given (instead of when) + + const expectedPassword = "zcvs"; + const expectedRepeatPassword = "zcvs4"; + + // when + + passwordInput.value = expectedPassword; + passwordInput.dispatchEvent(new Event('input')); + repeatPasswordInput.value = expectedRepeatPassword; + repeatPasswordInput.dispatchEvent(new Event('input')); + + window.dispatchEvent(new KeyboardEvent('keypress',{ + key: 'tab' + })); + + fixture.detectChanges(); + + const erElement: HTMLElement = hostDe.query(By.css(".er")).nativeElement; + expect(erElement.textContent).toContain("Password mismatch"); + + expect(submitButton.disabled).withContext("submitButton.disabled === false").toEqual(true); + expect(component.registrationFormGroup.invalid).withContext("submitButton.disabled === false").toEqual(true); + + // then (instead of verify) + + + }); + +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.component.ts new file mode 100644 index 0000000..efd9765 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.component.ts @@ -0,0 +1,183 @@ +import { Component, AfterViewInit, ElementRef, HostListener, OnDestroy, OnInit, QueryList, Renderer2, ViewChild, ViewChildren } from '@angular/core'; +import { AbstractControl, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; +import { MatTooltip } from '@angular/material/tooltip'; +import { Subject, first, takeUntil } from 'rxjs'; +import { RegistrationService } from './registration.service'; +import { HttpErrorResponse } from '@angular/common/http'; +import { GenerationCookieCsrfService } from '../generation-cookie-csrf.service'; +import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar'; + +@Component({ + selector: 'app-registration', + templateUrl: './registration.component.html', + styleUrls: ['./registration.component.scss'] +}) +export class RegistrationComponent implements AfterViewInit, OnDestroy { + // to clear the rxjs memory + // для очистки памяти rxjs + private readonly onDestroy = new Subject(); + + readonly registrationFormGroup = new FormGroup({ + login: new FormControl('', { + nonNullable: true, validators: [ + Validators.required, + Validators.minLength(4) + ] + }), + password: new FormControl('', { + nonNullable: true, validators: [ + Validators.required, + Validators.pattern('^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$') + ] + }), + repeatPassword: new FormControl('', { nonNullable: true, validators: [ + Validators.required + ]}), + orgName: new FormControl('', { + nonNullable: true, validators: [ + Validators.required, + Validators.minLength(4) + ] + }) + }, { validators: repeatPasswordEqPasswordValidator }); + + get login() { return this.registrationFormGroup.controls.login } + get password() { return this.registrationFormGroup.controls.password } + get repeatPassword() { return this.registrationFormGroup.controls.repeatPassword } + get orgName() { return this.registrationFormGroup.controls.orgName } + + // show or hide password + // показывать или скрывать пароль + passwordShowFlag: boolean = false; + passwordShowFlag2: boolean = false; + + // waiting for a response from the server + // ожидание ответа от сервера + getResponseFromServerFlag: boolean = false; + // error from server + // ошибка от сервера + errorOnServer!: string | undefined; + + readonly actionCloseButtonName = 'CLOSE'; + readonly confOptionsSnackbar: MatSnackBarConfig = { + duration: 5000 + }; + + @HostListener('window:keydown.enter', ['$event']) onKeydownEnterHandler(event: KeyboardEvent) { + this.submit(); + } + + @HostListener('window:keydown', ['$event']) onKeydownHandler(event: KeyboardEvent) { + if (!event.ctrlKey) { return; } + + if (event.key === 'l' || event.key === 'L' || event.key === 'д' || event.key === 'Д') { + this.focusLogin(); + return false; + } else if (event.key === 'p' || event.key === 'P' || event.key === 'З' || event.key === 'з') { + this.renderer.selectRootElement("#password")?.focus(); + return false; + } else if (event.key === 'r' || event.key === 'R' || event.key === 'К' || event.key === 'к') { + this.renderer.selectRootElement("#repeatPassword")?.focus(); + return false; + } else if (event.key === 'o' || event.key === 'O' || event.key === 'щ' || event.key === 'Щ') { + this.renderer.selectRootElement("#orgName")?.focus(); + return false; + } + + return; + } + + constructor( + private renderer: Renderer2, + private generationCookieCsrfService: GenerationCookieCsrfService, + private matSnackBar: MatSnackBar, + private registationService: RegistrationService + ) { } + + ngAfterViewInit() { + setTimeout(() => { + this.focusLogin(); + }, 1); + } + + focusLogin() { + this.renderer.selectRootElement("#login")?.focus(); + } + + + passwordDisplay(tooltip: MatTooltip) { + this.passwordShowFlag = !this.passwordShowFlag; + + if (this.passwordShowFlag) { + tooltip.message = 'Hide Password'; + } else { + tooltip.message = 'Show Password'; + } + tooltip.show(); + } + + passwordDisplay2(tooltip: MatTooltip) { + this.passwordShowFlag2 = !this.passwordShowFlag2; + + if (this.passwordShowFlag2) { + tooltip.message = 'Hide Password'; + } else { + tooltip.message = 'Show Password'; + } + tooltip.show(); + } + + submit(){ + + if (this.registrationFormGroup?.valid && !this.getResponseFromServerFlag) { + + this.getResponseFromServerFlag = true; + + this.generationCookieCsrfService.generateCookieCsrf(); + + this.registationService.registration( + this.registrationFormGroup.controls.login.value, + this.registrationFormGroup.controls.password.value, + this.registrationFormGroup.controls.orgName.value + ) + .pipe( + first(), + takeUntil(this.onDestroy) + ) + .subscribe({ + next: (responseLogin) => { + + // ответ получили + this.getResponseFromServerFlag = false; + + this.registrationFormGroup.reset(); + setTimeout(() => { + this.focusLogin(); + }, 5); + + this.matSnackBar.open("Success", this.actionCloseButtonName, this.confOptionsSnackbar); + }, + error: (error: HttpErrorResponse) => { + + // ответ получили + this.getResponseFromServerFlag = false; + } + }); + + } + } + + ngOnDestroy(): void { + this.onDestroy.next(); + this.onDestroy.complete(); + } +} + +const repeatPasswordEqPasswordValidator: ValidatorFn = +(control: AbstractControl): ValidationErrors | null => { + + const password = control.get('password'); + const repeatPassword = control.get('repeatPassword'); + + return password && repeatPassword && password.value !== repeatPassword.value ? { notEq: true } : null; +}; \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.module.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.module.ts new file mode 100644 index 0000000..8fc2996 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.module.ts @@ -0,0 +1,39 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { RegistrationRoutingModule } from './registration-routing.module'; +import { RegistrationComponent } from './registration.component'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; + +import { MatTooltipModule } from '@angular/material/tooltip'; +import { ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { RegistrationService } from './registration.service'; +import { HttpClientModule } from '@angular/common/http'; + +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; + +import { MatSnackBarModule } from '@angular/material/snack-bar'; + +@NgModule({ + declarations: [ + RegistrationComponent + ], + imports: [ + CommonModule, + RegistrationRoutingModule, + MatFormFieldModule, + MatInputModule, + MatTooltipModule, + ReactiveFormsModule, + MatButtonModule, + MatProgressSpinnerModule, + HttpClientModule, + MatSnackBarModule + ], + providers:[ + RegistrationService + ] +}) +export class RegistrationModule { } diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.service.spec.ts new file mode 100644 index 0000000..df80294 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.service.spec.ts @@ -0,0 +1,128 @@ +import { TestBed } from '@angular/core/testing'; + +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { HttpErrorResponse } from '@angular/common/http'; + +import { RegistrationService } from './registration.service'; +import { ErrorService } from '../error.service'; +import { environment } from '../../environments/environment'; + +describe('RegistrationService', () => { + let service: RegistrationService; + let errorServiceSpy: jasmine.SpyObj; + let httpTestingController: HttpTestingController; + + beforeEach(() => { + + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + RegistrationService, + { + provide: ErrorService, useValue: jasmine.createSpyObj('ErrorService', ['handle']) + } + ] + }); + service = TestBed.inject(RegistrationService); + errorServiceSpy = TestBed.inject(ErrorService) as jasmine.SpyObj; + httpTestingController = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpTestingController.verify(); + }); + + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('registration should be succeed', done => { + // given (instead of when) + + const registrationHttpBody: { + login: string, + password: string, + orgName: string + } = { + login: "someLogin", + password: "4Jof0#@V4cTaFev0", + orgName: "someOrg" + }; + + const expectedUserId: number = 641; + + // when + + service.registration(registrationHttpBody.login, registrationHttpBody.password, registrationHttpBody.orgName) + .subscribe({ + next: actualUserId => { + expect(actualUserId).toEqual(expectedUserId); + done(); + }, + error: done.fail + }); + + const req = httpTestingController.expectOne(`${environment.apiUrl}/registration`); + + req.flush(expectedUserId); + + // then (instead of verify) + + expect(req.request.method).toEqual('POST'); + expect(req.request.body).toEqual(registrationHttpBody); + + }); + + it('registration should be fail', done => { + // given (instead of when) + + const registrationHttpURL = `${environment.apiUrl}/registration`; + + const registrationHttpBody: { + login: string, + password: string, + orgName: string + } = { + login: "someLogin", + password: "xxx", + orgName: "someOrg" + }; + + const expectedErrorResponse = new HttpErrorResponse({ + error: '["password is invalid"]', + url: registrationHttpURL, + status: 400, + statusText: 'Bad request' + }); + + // when + + service.registration(registrationHttpBody.login, registrationHttpBody.password, registrationHttpBody.orgName) + .subscribe({ + next: actualUserId => done.fail('data not expected'), + error: (actualErrorResponse: HttpErrorResponse) => { + expect(actualErrorResponse).toEqual(expectedErrorResponse); + done(); + } + }); + + + const req = httpTestingController.expectOne(registrationHttpURL); + + req.flush(expectedErrorResponse.error, expectedErrorResponse); + + // then (instead of verify) + + expect(req.request.method).toEqual('POST'); + expect(req.request.body).toEqual(registrationHttpBody); + + expect(errorServiceSpy.handle.calls.count()) + .toBe(1); + expect(errorServiceSpy.handle.calls.first().args) + .toEqual([ expectedErrorResponse.error ]); + }); + +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.service.ts new file mode 100644 index 0000000..413cd39 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/registration/registration.service.ts @@ -0,0 +1,32 @@ +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable, catchError, throwError } from 'rxjs'; +import { ErrorService } from '../error.service'; +import { environment } from '../../environments/environment'; + +@Injectable() +export class RegistrationService { + + constructor(private http: HttpClient, private errorService: ErrorService) { } + + registration( + login: string, + password: string, + orgName: string + ): Observable{ + return this.http.post(`${environment.apiUrl}/registration`, { + login: login, + password: password, + orgName: orgName + }) + .pipe( + catchError(this.errorHandler.bind(this)) + ); + } + + private errorHandler(error: HttpErrorResponse) { + this.errorService.handle(error.error) + return throwError(() => error) + } +} + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/window.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/window.service.ts new file mode 100644 index 0000000..7719b0d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/app/window.service.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class WindowService { + + get(): any{ + return window; + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/.gitkeep b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/cat_401.png b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/cat_401.png new file mode 100644 index 0000000..08c7ff3 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/cat_401.png differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/cat_logout.jpg b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/cat_logout.jpg new file mode 100644 index 0000000..ba5cc94 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/cat_logout.jpg differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/error.jpg b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/error.jpg new file mode 100644 index 0000000..5c162cd Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/error.jpg differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/logo.png b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/logo.png new file mode 100644 index 0000000..0ef3fde Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/logo.png differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/stav.jpg b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/stav.jpg new file mode 100644 index 0000000..79ecc44 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/assets/stav.jpg differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/environments/environment.development.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/environments/environment.development.ts new file mode 100644 index 0000000..58f1b1d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/environments/environment.development.ts @@ -0,0 +1,4 @@ +export const environment = { + production: false, + apiUrl: 'http://localhost:3000' +}; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/environments/environment.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/environments/environment.ts new file mode 100644 index 0000000..564476d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/environments/environment.ts @@ -0,0 +1,4 @@ +export const environment = { + production: true, + apiUrl: '/api' +}; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/favicon.ico b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/favicon.ico new file mode 100644 index 0000000..ee799c9 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/favicon.ico differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz0dL_nz.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz0dL_nz.woff2 new file mode 100644 index 0000000..b52eb7a Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz0dL_nz.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz4dL_nz.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz4dL_nz.woff2 new file mode 100644 index 0000000..b038adc Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz4dL_nz.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2 new file mode 100644 index 0000000..327bebc Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzQdL_nz.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzQdL_nz.woff2 new file mode 100644 index 0000000..d63fc4a Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzQdL_nz.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc-CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc-CsTKlA.woff2 new file mode 100644 index 0000000..44e685b Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc-CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc0CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc0CsTKlA.woff2 new file mode 100644 index 0000000..a852d38 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc0CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc3CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc3CsTKlA.woff2 new file mode 100644 index 0000000..7eb322e Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc3CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc6CsQ.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc6CsQ.woff2 new file mode 100644 index 0000000..0ff2f81 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc6CsQ.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc-CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc-CsTKlA.woff2 new file mode 100644 index 0000000..7de8183 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc-CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.woff2 new file mode 100644 index 0000000..e2c6e1b Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc3CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc3CsTKlA.woff2 new file mode 100644 index 0000000..ccda5b9 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc3CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc6CsQ.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc6CsQ.woff2 new file mode 100644 index 0000000..7d846b1 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc6CsQ.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 new file mode 100644 index 0000000..7f69a29 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2 new file mode 100644 index 0000000..23e4e3c Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 new file mode 100644 index 0000000..d930de9 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2 new file mode 100644 index 0000000..b6653fb Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2 new file mode 100644 index 0000000..33a8438 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 new file mode 100644 index 0000000..0177669 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2 new file mode 100644 index 0000000..bb9e774 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 new file mode 100644 index 0000000..fe58be2 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2 new file mode 100644 index 0000000..4d9bb7d Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2 new file mode 100644 index 0000000..e9e3430 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxIIzI.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxIIzI.woff2 new file mode 100644 index 0000000..a5cc283 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxIIzI.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2 new file mode 100644 index 0000000..1758b7c Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2 new file mode 100644 index 0000000..d1d8a40 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 new file mode 100644 index 0000000..1862df5 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xIIzI.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xIIzI.woff2 new file mode 100644 index 0000000..2741d4f Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xIIzI.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 new file mode 100644 index 0000000..0171146 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2 new file mode 100644 index 0000000..1db8422 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 new file mode 100644 index 0000000..6362d7f Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2 new file mode 100644 index 0000000..b4b5df7 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 new file mode 100644 index 0000000..530e22c Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2 new file mode 100644 index 0000000..507809d Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 new file mode 100644 index 0000000..ef8c883 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2 new file mode 100644 index 0000000..8323edd Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 new file mode 100644 index 0000000..7d3dc27 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 new file mode 100644 index 0000000..6679743 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 new file mode 100644 index 0000000..32b25ee Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 new file mode 100644 index 0000000..7445d92 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2 new file mode 100644 index 0000000..5d7fed5 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2 new file mode 100644 index 0000000..4e8fac6 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfBBc4.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfBBc4.woff2 new file mode 100644 index 0000000..802499d Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfBBc4.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2 new file mode 100644 index 0000000..8f77a78 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2 new file mode 100644 index 0000000..9ddedcd Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu4mxK.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu4mxK.woff2 new file mode 100644 index 0000000..1a53701 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu4mxK.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 new file mode 100644 index 0000000..de226b3 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2 new file mode 100644 index 0000000..e3eabe7 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 new file mode 100644 index 0000000..de83f9c Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-brands-400.ttf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-brands-400.ttf new file mode 100644 index 0000000..774d51a Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-brands-400.ttf differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-brands-400.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-brands-400.woff2 new file mode 100644 index 0000000..71e3185 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-brands-400.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-regular-400.ttf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-regular-400.ttf new file mode 100644 index 0000000..8a9d634 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-regular-400.ttf differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-regular-400.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-regular-400.woff2 new file mode 100644 index 0000000..7f02168 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-regular-400.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-solid-900.ttf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-solid-900.ttf new file mode 100644 index 0000000..993dbe1 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-solid-900.ttf differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-solid-900.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-solid-900.woff2 new file mode 100644 index 0000000..5c16cd3 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-solid-900.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-v4compatibility.ttf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-v4compatibility.ttf new file mode 100644 index 0000000..ab6ae22 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-v4compatibility.ttf differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-v4compatibility.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-v4compatibility.woff2 new file mode 100644 index 0000000..9027e38 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/fonts/fontawesome-free/fa-v4compatibility.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/index.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/index.html new file mode 100644 index 0000000..dd8a3e4 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/index.html @@ -0,0 +1,13 @@ + + + + + Horns and hooves + + + + + + + + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/main.server.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/main.server.ts new file mode 100644 index 0000000..4b9d4d1 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/main.server.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { config } from './app/app.config.server'; + +const bootstrap = () => bootstrapApplication(AppComponent, config); + +export default bootstrap; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/main.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/main.ts new file mode 100644 index 0000000..35b00f3 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/main.ts @@ -0,0 +1,6 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { AppComponent } from './app/app.component'; + +bootstrapApplication(AppComponent, appConfig) + .catch((err) => console.error(err)); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/_shame.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/_shame.scss new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/base/_typography.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/base/_typography.scss new file mode 100644 index 0000000..0ed46af --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/base/_typography.scss @@ -0,0 +1,434 @@ + + +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'), url(/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz0dL_nz.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'), url(/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzQdL_nz.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'), url(/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz4dL_nz.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'), url(/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: local('Roboto Light Italic'), local('Roboto-LightItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: local('Roboto Light Italic'), local('Roboto-LightItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: local('Roboto Light Italic'), local('Roboto-LightItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: local('Roboto Light Italic'), local('Roboto-LightItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: local('Roboto Italic'), local('Roboto-Italic'), url(/fonts/Roboto/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: local('Roboto Italic'), local('Roboto-Italic'), url(/fonts/Roboto/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: local('Roboto Italic'), local('Roboto-Italic'), url(/fonts/Roboto/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: local('Roboto Italic'), local('Roboto-Italic'), url(/fonts/Roboto/KFOkCnqEu92Fr1Mu51xIIzI.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc-CsTKlA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: local('Roboto Black Italic'), local('Roboto-BlackItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: local('Roboto Black Italic'), local('Roboto-BlackItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc-CsTKlA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: local('Roboto Black Italic'), local('Roboto-BlackItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: local('Roboto Black Italic'), local('Roboto-BlackItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin'), local('Roboto-Thin'), url(/fonts/Roboto/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin'), local('Roboto-Thin'), url(/fonts/Roboto/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin'), local('Roboto-Thin'), url(/fonts/Roboto/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin'), local('Roboto-Thin'), url(/fonts/Roboto/KFOkCnqEu92Fr1MmgVxIIzI.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: local('Roboto Light'), local('Roboto-Light'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: local('Roboto Light'), local('Roboto-Light'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: local('Roboto Light'), local('Roboto-Light'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: local('Roboto Light'), local('Roboto-Light'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Roboto'), local('Roboto-Regular'), url(/fonts/Roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Roboto'), local('Roboto-Regular'), url(/fonts/Roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Roboto'), local('Roboto-Regular'), url(/fonts/Roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Roboto'), local('Roboto-Regular'), url(/fonts/Roboto/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium'), local('Roboto-Medium'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium'), local('Roboto-Medium'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium'), local('Roboto-Medium'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium'), local('Roboto-Medium'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold'), local('Roboto-Bold'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold'), local('Roboto-Bold'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold'), local('Roboto-Bold'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold'), local('Roboto-Bold'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: local('Roboto Black'), local('Roboto-Black'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: local('Roboto Black'), local('Roboto-Black'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: local('Roboto Black'), local('Roboto-Black'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: local('Roboto Black'), local('Roboto-Black'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/_material_io.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/_material_io.scss new file mode 100644 index 0000000..2c88a9a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/_material_io.scss @@ -0,0 +1,35 @@ +// Custom Theming for Angular Material +// For more information: https://material.angular.io/guide/theming +@use '@angular/material' as mat; +// Plus imports for other components in your app. + +// Include the common styles for Angular Material. We include this here so that you only +// have to load a single css file for Angular Material in your app. +// Be sure that you only ever include this mixin once! +@include mat.core(); + +// Define the palettes for your theme using the Material Design palettes available in palette.scss +// (imported above). For each palette, you can optionally specify a default, lighter, and darker +// hue. Available color palettes: https://material.io/design/color/ +$authorization-frontend-primary: mat.define-palette(mat.$indigo-palette); +$authorization-frontend-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); + +// The warn palette is optional (defaults to red). +$authorization-frontend-warn: mat.define-palette(mat.$red-palette); + +// Create the theme object. A theme consists of configurations for individual +// theming systems such as "color" or "typography". +$authorization-frontend-theme: mat.define-light-theme(( + color: ( + primary: $authorization-frontend-primary, + accent: $authorization-frontend-accent, + warn: $authorization-frontend-warn, + ) +)); + +// Include theme styles for core and each component used in your app. +// Alternatively, you can import and @include the theme mixins for each component +// that you are using. +@include mat.all-component-themes($authorization-frontend-theme); + +/* You can add global styles to this file, and also import other style files */ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_animated.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_animated.scss new file mode 100644 index 0000000..93555b2 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_animated.scss @@ -0,0 +1,153 @@ +// animating icons +// -------------------------- + +.#{$fa-css-prefix}-beat { + animation-name: #{$fa-css-prefix}-beat; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, ease-in-out); +} + +.#{$fa-css-prefix}-bounce { + animation-name: #{$fa-css-prefix}-bounce; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(0.280, 0.840, 0.420, 1)); +} + +.#{$fa-css-prefix}-fade { + animation-name: #{$fa-css-prefix}-fade; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(.4,0,.6,1)); +} + +.#{$fa-css-prefix}-beat-fade { + animation-name: #{$fa-css-prefix}-beat-fade; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(.4,0,.6,1)); +} + +.#{$fa-css-prefix}-flip { + animation-name: #{$fa-css-prefix}-flip; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, ease-in-out); +} + +.#{$fa-css-prefix}-shake { + animation-name: #{$fa-css-prefix}-shake; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, linear); +} + +.#{$fa-css-prefix}-spin { + animation-name: #{$fa-css-prefix}-spin; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 2s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, linear); +} + +.#{$fa-css-prefix}-spin-reverse { + --#{$fa-css-prefix}-animation-direction: reverse; +} + +.#{$fa-css-prefix}-pulse, +.#{$fa-css-prefix}-spin-pulse { + animation-name: #{$fa-css-prefix}-spin; + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, steps(8)); +} + +// if agent or operating system prefers reduced motion, disable animations +// see: https://www.smashingmagazine.com/2020/09/design-reduced-motion-sensitivities/ +// see: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion +@media (prefers-reduced-motion: reduce) { + .#{$fa-css-prefix}-beat, + .#{$fa-css-prefix}-bounce, + .#{$fa-css-prefix}-fade, + .#{$fa-css-prefix}-beat-fade, + .#{$fa-css-prefix}-flip, + .#{$fa-css-prefix}-pulse, + .#{$fa-css-prefix}-shake, + .#{$fa-css-prefix}-spin, + .#{$fa-css-prefix}-spin-pulse { + animation-delay: -1ms; + animation-duration: 1ms; + animation-iteration-count: 1; + transition-delay: 0s; + transition-duration: 0s; + } +} + +@keyframes #{$fa-css-prefix}-beat { + 0%, 90% { transform: scale(1); } + 45% { transform: scale(var(--#{$fa-css-prefix}-beat-scale, 1.25)); } +} + +@keyframes #{$fa-css-prefix}-bounce { + 0% { transform: scale(1,1) translateY(0); } + 10% { transform: scale(var(--#{$fa-css-prefix}-bounce-start-scale-x, 1.1),var(--#{$fa-css-prefix}-bounce-start-scale-y, 0.9)) translateY(0); } + 30% { transform: scale(var(--#{$fa-css-prefix}-bounce-jump-scale-x, 0.9),var(--#{$fa-css-prefix}-bounce-jump-scale-y, 1.1)) translateY(var(--#{$fa-css-prefix}-bounce-height, -0.5em)); } + 50% { transform: scale(var(--#{$fa-css-prefix}-bounce-land-scale-x, 1.05),var(--#{$fa-css-prefix}-bounce-land-scale-y, 0.95)) translateY(0); } + 57% { transform: scale(1,1) translateY(var(--#{$fa-css-prefix}-bounce-rebound, -0.125em)); } + 64% { transform: scale(1,1) translateY(0); } + 100% { transform: scale(1,1) translateY(0); } +} + +@keyframes #{$fa-css-prefix}-fade { + 50% { opacity: var(--#{$fa-css-prefix}-fade-opacity, 0.4); } +} + +@keyframes #{$fa-css-prefix}-beat-fade { + 0%, 100% { + opacity: var(--#{$fa-css-prefix}-beat-fade-opacity, 0.4); + transform: scale(1); + } + 50% { + opacity: 1; + transform: scale(var(--#{$fa-css-prefix}-beat-fade-scale, 1.125)); + } +} + +@keyframes #{$fa-css-prefix}-flip { + 50% { + transform: rotate3d(var(--#{$fa-css-prefix}-flip-x, 0), var(--#{$fa-css-prefix}-flip-y, 1), var(--#{$fa-css-prefix}-flip-z, 0), var(--#{$fa-css-prefix}-flip-angle, -180deg)); + } +} + +@keyframes #{$fa-css-prefix}-shake { + 0% { transform: rotate(-15deg); } + 4% { transform: rotate(15deg); } + 8%, 24% { transform: rotate(-18deg); } + 12%, 28% { transform: rotate(18deg); } + 16% { transform: rotate(-22deg); } + 20% { transform: rotate(22deg); } + 32% { transform: rotate(-12deg); } + 36% { transform: rotate(12deg); } + 40%, 100% { transform: rotate(0deg); } +} + +@keyframes #{$fa-css-prefix}-spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_bordered-pulled.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_bordered-pulled.scss new file mode 100644 index 0000000..9068253 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_bordered-pulled.scss @@ -0,0 +1,20 @@ +// bordered + pulled icons +// ------------------------- + +.#{$fa-css-prefix}-border { + border-color: var(--#{$fa-css-prefix}-border-color, #{$fa-border-color}); + border-radius: var(--#{$fa-css-prefix}-border-radius, #{$fa-border-radius}); + border-style: var(--#{$fa-css-prefix}-border-style, #{$fa-border-style}); + border-width: var(--#{$fa-css-prefix}-border-width, #{$fa-border-width}); + padding: var(--#{$fa-css-prefix}-border-padding, #{$fa-border-padding}); +} + +.#{$fa-css-prefix}-pull-left { + float: left; + margin-right: var(--#{$fa-css-prefix}-pull-margin, #{$fa-pull-margin}); +} + +.#{$fa-css-prefix}-pull-right { + float: right; + margin-left: var(--#{$fa-css-prefix}-pull-margin, #{$fa-pull-margin}); +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_core.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_core.scss new file mode 100644 index 0000000..1b2fd99 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_core.scss @@ -0,0 +1,43 @@ +// base icon class definition +// ------------------------- + +.#{$fa-css-prefix} { + font-family: var(--#{$fa-css-prefix}-style-family, '#{$fa-style-family}'); + font-weight: var(--#{$fa-css-prefix}-style, #{$fa-style}); +} + +.#{$fa-css-prefix}, +.#{$fa-css-prefix}-classic, +.#{$fa-css-prefix}-sharp, +.fas, +.#{$fa-css-prefix}-solid, +.far, +.#{$fa-css-prefix}-regular, +.fab, +.#{$fa-css-prefix}-brands { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: var(--#{$fa-css-prefix}-display, #{$fa-display}); + font-style: normal; + font-variant: normal; + line-height: 1; + text-rendering: auto; +} + +.fas, +.#{$fa-css-prefix}-classic, +.#{$fa-css-prefix}-solid, +.far, +.#{$fa-css-prefix}-regular { + font-family: 'Font Awesome 6 Free'; +} + +.fab, +.#{$fa-css-prefix}-brands { + font-family: 'Font Awesome 6 Brands'; +} + + +%fa-icon { + @include fa-icon; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_fixed-width.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_fixed-width.scss new file mode 100644 index 0000000..7234236 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_fixed-width.scss @@ -0,0 +1,7 @@ +// fixed-width icons +// ------------------------- + +.#{$fa-css-prefix}-fw { + text-align: center; + width: $fa-fw-width; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_functions.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_functions.scss new file mode 100644 index 0000000..a17ffe8 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_functions.scss @@ -0,0 +1,57 @@ +// functions +// -------------------------- + +// fa-content: convenience function used to set content property +@function fa-content($fa-var) { + @return unquote("\"#{ $fa-var }\""); +} + +// fa-divide: Originally obtained from the Bootstrap https://github.com/twbs/bootstrap +// +// Licensed under: The MIT License (MIT) +// +// Copyright (c) 2011-2021 Twitter, Inc. +// Copyright (c) 2011-2021 The Bootstrap Authors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +@function fa-divide($dividend, $divisor, $precision: 10) { + $sign: if($dividend > 0 and $divisor > 0, 1, -1); + $dividend: abs($dividend); + $divisor: abs($divisor); + $quotient: 0; + $remainder: $dividend; + @if $dividend == 0 { + @return 0; + } + @if $divisor == 0 { + @error "Cannot divide by 0"; + } + @if $divisor == 1 { + @return $dividend; + } + @while $remainder >= $divisor { + $quotient: $quotient + 1; + $remainder: $remainder - $divisor; + } + @if $remainder > 0 and $precision > 0 { + $remainder: fa-divide($remainder * 10, $divisor, $precision - 1) * .1; + } + @return ($quotient + $remainder) * $sign; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_icons.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_icons.scss new file mode 100644 index 0000000..0f55926 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_icons.scss @@ -0,0 +1,10 @@ +// specific icon class definition +// ------------------------- + +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen +readers do not read off random characters that represent icons */ + +@each $name, $icon in $fa-icons { + .#{$fa-css-prefix}-#{$name}::before { content: unquote("\"#{ $icon }\""); } +} + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_list.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_list.scss new file mode 100644 index 0000000..ced36e2 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_list.scss @@ -0,0 +1,18 @@ +// icons in a list +// ------------------------- + +.#{$fa-css-prefix}-ul { + list-style-type: none; + margin-left: var(--#{$fa-css-prefix}-li-margin, #{$fa-li-margin}); + padding-left: 0; + + > li { position: relative; } +} + +.#{$fa-css-prefix}-li { + left: calc(var(--#{$fa-css-prefix}-li-width, #{$fa-li-width}) * -1); + position: absolute; + text-align: center; + width: var(--#{$fa-css-prefix}-li-width, #{$fa-li-width}); + line-height: inherit; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_mixins.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_mixins.scss new file mode 100644 index 0000000..e06b69a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_mixins.scss @@ -0,0 +1,75 @@ +// mixins +// -------------------------- + +// base rendering for an icon +@mixin fa-icon { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + display: inline-block; + font-style: normal; + font-variant: normal; + font-weight: normal; + line-height: 1; +} + +// sets relative font-sizing and alignment (in _sizing) +@mixin fa-size ($font-size) { + font-size: fa-divide($font-size, $fa-size-scale-base) * 1em; // converts step in sizing scale into an em-based value that's relative to the scale's base + line-height: fa-divide(1, $font-size) * 1em; // sets the line-height of the icon back to that of it's parent + vertical-align: (fa-divide(6, $font-size) - fa-divide(3, 8)) * 1em; // vertically centers the icon taking into account the surrounding text's descender +} + +// only display content to screen readers +// see: https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/ +// see: https://hugogiraudel.com/2016/10/13/css-hide-and-seek/ +@mixin fa-sr-only() { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +// use in conjunction with .sr-only to only display content when it's focused +@mixin fa-sr-only-focusable() { + &:not(:focus) { + @include fa-sr-only(); + } +} + +// sets a specific icon family to use alongside style + icon mixins + +// convenience mixins for declaring pseudo-elements by CSS variable, +// including all style-specific font properties, and both the ::before +// and ::after elements in the duotone case. +@mixin fa-icon-solid($fa-var) { + @extend %fa-icon; + @extend .fa-solid; + + &::before { + content: unquote("\"#{ $fa-var }\""); + } +} + +@mixin fa-icon-regular($fa-var) { + @extend %fa-icon; + @extend .fa-regular; + + &::before { + content: unquote("\"#{ $fa-var }\""); + } +} + +@mixin fa-icon-brands($fa-var) { + @extend %fa-icon; + @extend .fa-brands; + + &::before { + content: unquote("\"#{ $fa-var }\""); + } +} + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_rotated-flipped.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_rotated-flipped.scss new file mode 100644 index 0000000..f27fabe --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_rotated-flipped.scss @@ -0,0 +1,31 @@ +// rotating + flipping icons +// ------------------------- + +.#{$fa-css-prefix}-rotate-90 { + transform: rotate(90deg); +} + +.#{$fa-css-prefix}-rotate-180 { + transform: rotate(180deg); +} + +.#{$fa-css-prefix}-rotate-270 { + transform: rotate(270deg); +} + +.#{$fa-css-prefix}-flip-horizontal { + transform: scale(-1, 1); +} + +.#{$fa-css-prefix}-flip-vertical { + transform: scale(1, -1); +} + +.#{$fa-css-prefix}-flip-both, +.#{$fa-css-prefix}-flip-horizontal.#{$fa-css-prefix}-flip-vertical { + transform: scale(-1, -1); +} + +.#{$fa-css-prefix}-rotate-by { + transform: rotate(var(--#{$fa-css-prefix}-rotate-angle, none)); +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_screen-reader.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_screen-reader.scss new file mode 100644 index 0000000..2beb887 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_screen-reader.scss @@ -0,0 +1,14 @@ +// screen-reader utilities +// ------------------------- + +// only display content to screen readers +.sr-only, +.#{$fa-css-prefix}-sr-only { + @include fa-sr-only; +} + +// use in conjunction with .sr-only to only display content when it's focused +.sr-only-focusable, +.#{$fa-css-prefix}-sr-only-focusable { + @include fa-sr-only-focusable; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_shims.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_shims.scss new file mode 100644 index 0000000..7809aa6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_shims.scss @@ -0,0 +1,2042 @@ +.#{$fa-css-prefix}.#{$fa-css-prefix}-glass:before { content: unquote("\"#{ $fa-var-martini-glass-empty }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-o:before { content: unquote("\"#{ $fa-var-envelope }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-o:before { content: unquote("\"#{ $fa-var-star }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-remove:before { content: unquote("\"#{ $fa-var-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-close:before { content: unquote("\"#{ $fa-var-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gear:before { content: unquote("\"#{ $fa-var-gear }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-trash-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-trash-o:before { content: unquote("\"#{ $fa-var-trash-can }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-home:before { content: unquote("\"#{ $fa-var-house }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-o:before { content: unquote("\"#{ $fa-var-file }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-clock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-clock-o:before { content: unquote("\"#{ $fa-var-clock }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-down:before { content: unquote("\"#{ $fa-var-circle-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-up:before { content: unquote("\"#{ $fa-var-circle-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-play-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-play-circle-o:before { content: unquote("\"#{ $fa-var-circle-play }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-repeat:before { content: unquote("\"#{ $fa-var-arrow-rotate-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rotate-right:before { content: unquote("\"#{ $fa-var-arrow-rotate-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-refresh:before { content: unquote("\"#{ $fa-var-arrows-rotate }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-list-alt { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-list-alt:before { content: unquote("\"#{ $fa-var-rectangle-list }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dedent:before { content: unquote("\"#{ $fa-var-outdent }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-video-camera:before { content: unquote("\"#{ $fa-var-video }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-picture-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-picture-o:before { content: unquote("\"#{ $fa-var-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-photo { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-photo:before { content: unquote("\"#{ $fa-var-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-image { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-image:before { content: unquote("\"#{ $fa-var-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-map-marker:before { content: unquote("\"#{ $fa-var-location-dot }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pencil-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pencil-square-o:before { content: unquote("\"#{ $fa-var-pen-to-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-edit { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-edit:before { content: unquote("\"#{ $fa-var-pen-to-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-share-square-o:before { content: unquote("\"#{ $fa-var-share-from-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-square-o:before { content: unquote("\"#{ $fa-var-square-check }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows:before { content: unquote("\"#{ $fa-var-up-down-left-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-circle-o:before { content: unquote("\"#{ $fa-var-circle-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-circle-o:before { content: unquote("\"#{ $fa-var-circle-check }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mail-forward:before { content: unquote("\"#{ $fa-var-share }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-expand:before { content: unquote("\"#{ $fa-var-up-right-and-down-left-from-center }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-compress:before { content: unquote("\"#{ $fa-var-down-left-and-up-right-to-center }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eye { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eye-slash { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-warning:before { content: unquote("\"#{ $fa-var-triangle-exclamation }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar:before { content: unquote("\"#{ $fa-var-calendar-days }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows-v:before { content: unquote("\"#{ $fa-var-up-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows-h:before { content: unquote("\"#{ $fa-var-left-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bar-chart:before { content: unquote("\"#{ $fa-var-chart-column }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bar-chart-o:before { content: unquote("\"#{ $fa-var-chart-column }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitter-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitter-square:before { content: unquote("\"#{ $fa-var-square-twitter }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-square:before { content: unquote("\"#{ $fa-var-square-facebook }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gears:before { content: unquote("\"#{ $fa-var-gears }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-up:before { content: unquote("\"#{ $fa-var-thumbs-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-down:before { content: unquote("\"#{ $fa-var-thumbs-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-heart-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-heart-o:before { content: unquote("\"#{ $fa-var-heart }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sign-out:before { content: unquote("\"#{ $fa-var-right-from-bracket }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin-square:before { content: unquote("\"#{ $fa-var-linkedin }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumb-tack:before { content: unquote("\"#{ $fa-var-thumbtack }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-external-link:before { content: unquote("\"#{ $fa-var-up-right-from-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sign-in:before { content: unquote("\"#{ $fa-var-right-to-bracket }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-github-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-github-square:before { content: unquote("\"#{ $fa-var-square-github }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-lemon-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-lemon-o:before { content: unquote("\"#{ $fa-var-lemon }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-square-o:before { content: unquote("\"#{ $fa-var-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bookmark-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bookmark-o:before { content: unquote("\"#{ $fa-var-bookmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitter { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook:before { content: unquote("\"#{ $fa-var-facebook-f }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-f { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-f:before { content: unquote("\"#{ $fa-var-facebook-f }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-github { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-credit-card { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-feed:before { content: unquote("\"#{ $fa-var-rss }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hdd-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hdd-o:before { content: unquote("\"#{ $fa-var-hard-drive }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-right:before { content: unquote("\"#{ $fa-var-hand-point-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-left:before { content: unquote("\"#{ $fa-var-hand-point-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-up:before { content: unquote("\"#{ $fa-var-hand-point-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-down:before { content: unquote("\"#{ $fa-var-hand-point-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-globe:before { content: unquote("\"#{ $fa-var-earth-americas }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tasks:before { content: unquote("\"#{ $fa-var-bars-progress }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows-alt:before { content: unquote("\"#{ $fa-var-maximize }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-group:before { content: unquote("\"#{ $fa-var-users }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-chain:before { content: unquote("\"#{ $fa-var-link }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cut:before { content: unquote("\"#{ $fa-var-scissors }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-files-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-files-o:before { content: unquote("\"#{ $fa-var-copy }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-floppy-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-floppy-o:before { content: unquote("\"#{ $fa-var-floppy-disk }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-save { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-save:before { content: unquote("\"#{ $fa-var-floppy-disk }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-navicon:before { content: unquote("\"#{ $fa-var-bars }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-reorder:before { content: unquote("\"#{ $fa-var-bars }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-magic:before { content: unquote("\"#{ $fa-var-wand-magic-sparkles }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest-square:before { content: unquote("\"#{ $fa-var-square-pinterest }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-square:before { content: unquote("\"#{ $fa-var-square-google-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus:before { content: unquote("\"#{ $fa-var-google-plus-g }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-money:before { content: unquote("\"#{ $fa-var-money-bill-1 }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-unsorted:before { content: unquote("\"#{ $fa-var-sort }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-desc:before { content: unquote("\"#{ $fa-var-sort-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-asc:before { content: unquote("\"#{ $fa-var-sort-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin:before { content: unquote("\"#{ $fa-var-linkedin-in }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rotate-left:before { content: unquote("\"#{ $fa-var-arrow-rotate-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-legal:before { content: unquote("\"#{ $fa-var-gavel }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tachometer:before { content: unquote("\"#{ $fa-var-gauge-high }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dashboard:before { content: unquote("\"#{ $fa-var-gauge-high }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-comment-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-comment-o:before { content: unquote("\"#{ $fa-var-comment }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-comments-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-comments-o:before { content: unquote("\"#{ $fa-var-comments }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-flash:before { content: unquote("\"#{ $fa-var-bolt }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-clipboard:before { content: unquote("\"#{ $fa-var-paste }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-lightbulb-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-lightbulb-o:before { content: unquote("\"#{ $fa-var-lightbulb }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-exchange:before { content: unquote("\"#{ $fa-var-right-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cloud-download:before { content: unquote("\"#{ $fa-var-cloud-arrow-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cloud-upload:before { content: unquote("\"#{ $fa-var-cloud-arrow-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-o:before { content: unquote("\"#{ $fa-var-bell }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cutlery:before { content: unquote("\"#{ $fa-var-utensils }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-text-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-text-o:before { content: unquote("\"#{ $fa-var-file-lines }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-building-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-building-o:before { content: unquote("\"#{ $fa-var-building }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hospital-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hospital-o:before { content: unquote("\"#{ $fa-var-hospital }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tablet:before { content: unquote("\"#{ $fa-var-tablet-screen-button }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mobile:before { content: unquote("\"#{ $fa-var-mobile-screen-button }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mobile-phone:before { content: unquote("\"#{ $fa-var-mobile-screen-button }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-o:before { content: unquote("\"#{ $fa-var-circle }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mail-reply:before { content: unquote("\"#{ $fa-var-reply }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-github-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-o:before { content: unquote("\"#{ $fa-var-folder }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-open-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-open-o:before { content: unquote("\"#{ $fa-var-folder-open }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-smile-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-smile-o:before { content: unquote("\"#{ $fa-var-face-smile }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-frown-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-frown-o:before { content: unquote("\"#{ $fa-var-face-frown }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-meh-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-meh-o:before { content: unquote("\"#{ $fa-var-face-meh }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-keyboard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-keyboard-o:before { content: unquote("\"#{ $fa-var-keyboard }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-flag-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-flag-o:before { content: unquote("\"#{ $fa-var-flag }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mail-reply-all:before { content: unquote("\"#{ $fa-var-reply-all }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-o:before { content: unquote("\"#{ $fa-var-star-half-stroke }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-empty { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-empty:before { content: unquote("\"#{ $fa-var-star-half-stroke }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-full { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-full:before { content: unquote("\"#{ $fa-var-star-half-stroke }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-code-fork:before { content: unquote("\"#{ $fa-var-code-branch }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-chain-broken:before { content: unquote("\"#{ $fa-var-link-slash }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-unlink:before { content: unquote("\"#{ $fa-var-link-slash }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-o:before { content: unquote("\"#{ $fa-var-calendar }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-maxcdn { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-html5 { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-css3 { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-unlock-alt:before { content: unquote("\"#{ $fa-var-unlock }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-minus-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-minus-square-o:before { content: unquote("\"#{ $fa-var-square-minus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-level-up:before { content: unquote("\"#{ $fa-var-turn-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-level-down:before { content: unquote("\"#{ $fa-var-turn-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pencil-square:before { content: unquote("\"#{ $fa-var-square-pen }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-external-link-square:before { content: unquote("\"#{ $fa-var-square-up-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-compass { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-down:before { content: unquote("\"#{ $fa-var-square-caret-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-down:before { content: unquote("\"#{ $fa-var-square-caret-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-up:before { content: unquote("\"#{ $fa-var-square-caret-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-up:before { content: unquote("\"#{ $fa-var-square-caret-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-right:before { content: unquote("\"#{ $fa-var-square-caret-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-right:before { content: unquote("\"#{ $fa-var-square-caret-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eur:before { content: unquote("\"#{ $fa-var-euro-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-euro:before { content: unquote("\"#{ $fa-var-euro-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gbp:before { content: unquote("\"#{ $fa-var-sterling-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-usd:before { content: unquote("\"#{ $fa-var-dollar-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dollar:before { content: unquote("\"#{ $fa-var-dollar-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-inr:before { content: unquote("\"#{ $fa-var-indian-rupee-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rupee:before { content: unquote("\"#{ $fa-var-indian-rupee-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-jpy:before { content: unquote("\"#{ $fa-var-yen-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cny:before { content: unquote("\"#{ $fa-var-yen-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rmb:before { content: unquote("\"#{ $fa-var-yen-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yen:before { content: unquote("\"#{ $fa-var-yen-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rub:before { content: unquote("\"#{ $fa-var-ruble-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ruble:before { content: unquote("\"#{ $fa-var-ruble-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rouble:before { content: unquote("\"#{ $fa-var-ruble-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-krw:before { content: unquote("\"#{ $fa-var-won-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-won:before { content: unquote("\"#{ $fa-var-won-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-btc { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitcoin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitcoin:before { content: unquote("\"#{ $fa-var-btc }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-text:before { content: unquote("\"#{ $fa-var-file-lines }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-alpha-asc:before { content: unquote("\"#{ $fa-var-arrow-down-a-z }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-alpha-desc:before { content: unquote("\"#{ $fa-var-arrow-down-z-a }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-amount-asc:before { content: unquote("\"#{ $fa-var-arrow-down-short-wide }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-amount-desc:before { content: unquote("\"#{ $fa-var-arrow-down-wide-short }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-numeric-asc:before { content: unquote("\"#{ $fa-var-arrow-down-1-9 }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-numeric-desc:before { content: unquote("\"#{ $fa-var-arrow-down-9-1 }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-square:before { content: unquote("\"#{ $fa-var-square-youtube }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-xing { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-xing-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-xing-square:before { content: unquote("\"#{ $fa-var-square-xing }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-play { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-play:before { content: unquote("\"#{ $fa-var-youtube }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dropbox { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stack-overflow { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-instagram { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-flickr { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-adn { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitbucket { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitbucket-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitbucket-square:before { content: unquote("\"#{ $fa-var-bitbucket }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tumblr { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tumblr-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-tumblr-square:before { content: unquote("\"#{ $fa-var-square-tumblr }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-down:before { content: unquote("\"#{ $fa-var-down-long }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-up:before { content: unquote("\"#{ $fa-var-up-long }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-left:before { content: unquote("\"#{ $fa-var-left-long }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-right:before { content: unquote("\"#{ $fa-var-right-long }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-apple { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-windows { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-android { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-linux { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dribbble { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-skype { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-foursquare { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-trello { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gratipay { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gittip { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-gittip:before { content: unquote("\"#{ $fa-var-gratipay }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sun-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-sun-o:before { content: unquote("\"#{ $fa-var-sun }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-moon-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-moon-o:before { content: unquote("\"#{ $fa-var-moon }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vk { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-weibo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-renren { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pagelines { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stack-exchange { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-right:before { content: unquote("\"#{ $fa-var-circle-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-left:before { content: unquote("\"#{ $fa-var-circle-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-left:before { content: unquote("\"#{ $fa-var-square-caret-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-left:before { content: unquote("\"#{ $fa-var-square-caret-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dot-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-dot-circle-o:before { content: unquote("\"#{ $fa-var-circle-dot }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo-square:before { content: unquote("\"#{ $fa-var-square-vimeo }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-try:before { content: unquote("\"#{ $fa-var-turkish-lira-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-turkish-lira:before { content: unquote("\"#{ $fa-var-turkish-lira-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-plus-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-plus-square-o:before { content: unquote("\"#{ $fa-var-square-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-slack { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wordpress { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-openid { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-institution:before { content: unquote("\"#{ $fa-var-building-columns }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bank:before { content: unquote("\"#{ $fa-var-building-columns }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mortar-board:before { content: unquote("\"#{ $fa-var-graduation-cap }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yahoo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit-square:before { content: unquote("\"#{ $fa-var-square-reddit }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stumbleupon-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stumbleupon { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-delicious { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-digg { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pied-piper-pp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pied-piper-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-drupal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-joomla { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-behance { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-behance-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-behance-square:before { content: unquote("\"#{ $fa-var-square-behance }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-steam { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-steam-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-steam-square:before { content: unquote("\"#{ $fa-var-square-steam }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-automobile:before { content: unquote("\"#{ $fa-var-car }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cab:before { content: unquote("\"#{ $fa-var-taxi }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-spotify { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-deviantart { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-soundcloud { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-pdf-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-pdf-o:before { content: unquote("\"#{ $fa-var-file-pdf }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-word-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-word-o:before { content: unquote("\"#{ $fa-var-file-word }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-excel-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-excel-o:before { content: unquote("\"#{ $fa-var-file-excel }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-powerpoint-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-powerpoint-o:before { content: unquote("\"#{ $fa-var-file-powerpoint }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-image-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-image-o:before { content: unquote("\"#{ $fa-var-file-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-photo-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-photo-o:before { content: unquote("\"#{ $fa-var-file-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-picture-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-picture-o:before { content: unquote("\"#{ $fa-var-file-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-archive-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-archive-o:before { content: unquote("\"#{ $fa-var-file-zipper }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-zip-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-zip-o:before { content: unquote("\"#{ $fa-var-file-zipper }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-audio-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-audio-o:before { content: unquote("\"#{ $fa-var-file-audio }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-sound-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-sound-o:before { content: unquote("\"#{ $fa-var-file-audio }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-video-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-video-o:before { content: unquote("\"#{ $fa-var-file-video }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-movie-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-movie-o:before { content: unquote("\"#{ $fa-var-file-video }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-code-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-code-o:before { content: unquote("\"#{ $fa-var-file-code }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vine { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-codepen { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-jsfiddle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-life-bouy:before { content: unquote("\"#{ $fa-var-life-ring }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-life-buoy:before { content: unquote("\"#{ $fa-var-life-ring }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-life-saver:before { content: unquote("\"#{ $fa-var-life-ring }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-support:before { content: unquote("\"#{ $fa-var-life-ring }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-o-notch:before { content: unquote("\"#{ $fa-var-circle-notch }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rebel { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ra { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-ra:before { content: unquote("\"#{ $fa-var-rebel }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-resistance { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-resistance:before { content: unquote("\"#{ $fa-var-rebel }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-empire { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ge { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-ge:before { content: unquote("\"#{ $fa-var-empire }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-git-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-git-square:before { content: unquote("\"#{ $fa-var-square-git }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-git { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hacker-news { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-y-combinator-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-y-combinator-square:before { content: unquote("\"#{ $fa-var-hacker-news }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc-square:before { content: unquote("\"#{ $fa-var-hacker-news }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tencent-weibo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-qq { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-weixin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wechat { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wechat:before { content: unquote("\"#{ $fa-var-weixin }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-send:before { content: unquote("\"#{ $fa-var-paper-plane }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-paper-plane-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-paper-plane-o:before { content: unquote("\"#{ $fa-var-paper-plane }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-send-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-send-o:before { content: unquote("\"#{ $fa-var-paper-plane }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-thin { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-thin:before { content: unquote("\"#{ $fa-var-circle }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-header:before { content: unquote("\"#{ $fa-var-heading }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-futbol-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-futbol-o:before { content: unquote("\"#{ $fa-var-futbol }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-soccer-ball-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-soccer-ball-o:before { content: unquote("\"#{ $fa-var-futbol }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-slideshare { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitch { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yelp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-newspaper-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-newspaper-o:before { content: unquote("\"#{ $fa-var-newspaper }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-paypal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-wallet { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-visa { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-mastercard { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-discover { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-amex { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-paypal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-stripe { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-slash-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-slash-o:before { content: unquote("\"#{ $fa-var-bell-slash }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-trash:before { content: unquote("\"#{ $fa-var-trash-can }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-copyright { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eyedropper:before { content: unquote("\"#{ $fa-var-eye-dropper }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-area-chart:before { content: unquote("\"#{ $fa-var-chart-area }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pie-chart:before { content: unquote("\"#{ $fa-var-chart-pie }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-line-chart:before { content: unquote("\"#{ $fa-var-chart-line }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-lastfm { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-lastfm-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-lastfm-square:before { content: unquote("\"#{ $fa-var-square-lastfm }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ioxhost { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-angellist { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc:before { content: unquote("\"#{ $fa-var-closed-captioning }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ils:before { content: unquote("\"#{ $fa-var-shekel-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-shekel:before { content: unquote("\"#{ $fa-var-shekel-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sheqel:before { content: unquote("\"#{ $fa-var-shekel-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-buysellads { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-connectdevelop { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dashcube { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-forumbee { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-leanpub { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sellsy { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-shirtsinbulk { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-simplybuilt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-skyatlas { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-diamond { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-diamond:before { content: unquote("\"#{ $fa-var-gem }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-transgender:before { content: unquote("\"#{ $fa-var-mars-and-venus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-intersex:before { content: unquote("\"#{ $fa-var-mars-and-venus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-transgender-alt:before { content: unquote("\"#{ $fa-var-transgender }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-official { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-official:before { content: unquote("\"#{ $fa-var-facebook }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest-p { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-whatsapp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hotel:before { content: unquote("\"#{ $fa-var-bed }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-viacoin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-medium { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-y-combinator { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc:before { content: unquote("\"#{ $fa-var-y-combinator }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-optin-monster { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-opencart { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-expeditedssl { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-4:before { content: unquote("\"#{ $fa-var-battery-full }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery:before { content: unquote("\"#{ $fa-var-battery-full }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-3:before { content: unquote("\"#{ $fa-var-battery-three-quarters }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-2:before { content: unquote("\"#{ $fa-var-battery-half }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-1:before { content: unquote("\"#{ $fa-var-battery-quarter }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-0:before { content: unquote("\"#{ $fa-var-battery-empty }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-object-group { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-object-ungroup { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sticky-note-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-sticky-note-o:before { content: unquote("\"#{ $fa-var-note-sticky }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-jcb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-diners-club { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-clone { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-o:before { content: unquote("\"#{ $fa-var-hourglass }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-1:before { content: unquote("\"#{ $fa-var-hourglass-start }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-2:before { content: unquote("\"#{ $fa-var-hourglass-half }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-3:before { content: unquote("\"#{ $fa-var-hourglass-end }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-rock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-rock-o:before { content: unquote("\"#{ $fa-var-hand-back-fist }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-grab-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-grab-o:before { content: unquote("\"#{ $fa-var-hand-back-fist }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-paper-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-paper-o:before { content: unquote("\"#{ $fa-var-hand }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-stop-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-stop-o:before { content: unquote("\"#{ $fa-var-hand }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-scissors-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-scissors-o:before { content: unquote("\"#{ $fa-var-hand-scissors }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-lizard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-lizard-o:before { content: unquote("\"#{ $fa-var-hand-lizard }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-spock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-spock-o:before { content: unquote("\"#{ $fa-var-hand-spock }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-pointer-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-pointer-o:before { content: unquote("\"#{ $fa-var-hand-pointer }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-peace-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-peace-o:before { content: unquote("\"#{ $fa-var-hand-peace }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-registered { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-creative-commons { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gg { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gg-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-odnoklassniki { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-odnoklassniki-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-odnoklassniki-square:before { content: unquote("\"#{ $fa-var-square-odnoklassniki }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-get-pocket { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wikipedia-w { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-safari { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-chrome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-firefox { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-opera { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-internet-explorer { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-television:before { content: unquote("\"#{ $fa-var-tv }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-contao { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-500px { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-amazon { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-plus-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-plus-o:before { content: unquote("\"#{ $fa-var-calendar-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-minus-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-minus-o:before { content: unquote("\"#{ $fa-var-calendar-minus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-times-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-times-o:before { content: unquote("\"#{ $fa-var-calendar-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-check-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-check-o:before { content: unquote("\"#{ $fa-var-calendar-check }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-map-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-map-o:before { content: unquote("\"#{ $fa-var-map }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-commenting:before { content: unquote("\"#{ $fa-var-comment-dots }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-commenting-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-commenting-o:before { content: unquote("\"#{ $fa-var-comment-dots }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-houzz { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo:before { content: unquote("\"#{ $fa-var-vimeo-v }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-black-tie { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-fonticons { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit-alien { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-edge { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-credit-card-alt:before { content: unquote("\"#{ $fa-var-credit-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-codiepie { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-modx { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-fort-awesome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-usb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-product-hunt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mixcloud { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-scribd { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pause-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pause-circle-o:before { content: unquote("\"#{ $fa-var-circle-pause }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stop-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-stop-circle-o:before { content: unquote("\"#{ $fa-var-circle-stop }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bluetooth { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bluetooth-b { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gitlab { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wpbeginner { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wpforms { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-envira { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wheelchair-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wheelchair-alt:before { content: unquote("\"#{ $fa-var-accessible-icon }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-question-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-question-circle-o:before { content: unquote("\"#{ $fa-var-circle-question }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-volume-control-phone:before { content: unquote("\"#{ $fa-var-phone-volume }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-asl-interpreting:before { content: unquote("\"#{ $fa-var-hands-asl-interpreting }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-deafness:before { content: unquote("\"#{ $fa-var-ear-deaf }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hard-of-hearing:before { content: unquote("\"#{ $fa-var-ear-deaf }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-glide { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-glide-g { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-signing:before { content: unquote("\"#{ $fa-var-hands }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-viadeo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-viadeo-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-viadeo-square:before { content: unquote("\"#{ $fa-var-square-viadeo }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-ghost { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-ghost:before { content: unquote("\"#{ $fa-var-snapchat }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-square:before { content: unquote("\"#{ $fa-var-square-snapchat }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pied-piper { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-first-order { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yoast { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-themeisle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-official { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-official:before { content: unquote("\"#{ $fa-var-google-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-circle:before { content: unquote("\"#{ $fa-var-google-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-font-awesome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-fa { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-fa:before { content: unquote("\"#{ $fa-var-font-awesome }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-handshake-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-handshake-o:before { content: unquote("\"#{ $fa-var-handshake }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-open-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-open-o:before { content: unquote("\"#{ $fa-var-envelope-open }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-linode { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-book-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-book-o:before { content: unquote("\"#{ $fa-var-address-book }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vcard:before { content: unquote("\"#{ $fa-var-address-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-card-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-card-o:before { content: unquote("\"#{ $fa-var-address-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vcard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-vcard-o:before { content: unquote("\"#{ $fa-var-address-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-circle-o:before { content: unquote("\"#{ $fa-var-circle-user }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-o:before { content: unquote("\"#{ $fa-var-user }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-id-badge { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-drivers-license:before { content: unquote("\"#{ $fa-var-id-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-id-card-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-id-card-o:before { content: unquote("\"#{ $fa-var-id-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-drivers-license-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-drivers-license-o:before { content: unquote("\"#{ $fa-var-id-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-quora { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-free-code-camp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-telegram { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-4:before { content: unquote("\"#{ $fa-var-temperature-full }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer:before { content: unquote("\"#{ $fa-var-temperature-full }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-3:before { content: unquote("\"#{ $fa-var-temperature-three-quarters }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-2:before { content: unquote("\"#{ $fa-var-temperature-half }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-1:before { content: unquote("\"#{ $fa-var-temperature-quarter }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-0:before { content: unquote("\"#{ $fa-var-temperature-empty }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bathtub:before { content: unquote("\"#{ $fa-var-bath }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-s15:before { content: unquote("\"#{ $fa-var-bath }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-maximize { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-restore { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-rectangle:before { content: unquote("\"#{ $fa-var-rectangle-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-close-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-close-o:before { content: unquote("\"#{ $fa-var-rectangle-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-rectangle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-rectangle-o:before { content: unquote("\"#{ $fa-var-rectangle-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bandcamp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-grav { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-etsy { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-imdb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ravelry { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eercast { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-eercast:before { content: unquote("\"#{ $fa-var-sellcast }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-snowflake-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-snowflake-o:before { content: unquote("\"#{ $fa-var-snowflake }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-superpowers { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wpexplorer { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-meetup { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_sizing.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_sizing.scss new file mode 100644 index 0000000..e171e7d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_sizing.scss @@ -0,0 +1,16 @@ +// sizing icons +// ------------------------- + +// literal magnification scale +@for $i from 1 through 10 { + .#{$fa-css-prefix}-#{$i}x { + font-size: $i * 1em; + } +} + +// step-based scale (with alignment) +@each $size, $value in $fa-sizes { + .#{$fa-css-prefix}-#{$size} { + @include fa-size($value); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_stacked.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_stacked.scss new file mode 100644 index 0000000..d9a9d4e --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_stacked.scss @@ -0,0 +1,32 @@ +// stacking icons +// ------------------------- + +.#{$fa-css-prefix}-stack { + display: inline-block; + height: 2em; + line-height: 2em; + position: relative; + vertical-align: $fa-stack-vertical-align; + width: $fa-stack-width; +} + +.#{$fa-css-prefix}-stack-1x, +.#{$fa-css-prefix}-stack-2x { + left: 0; + position: absolute; + text-align: center; + width: 100%; + z-index: var(--#{$fa-css-prefix}-stack-z-index, #{$fa-stack-z-index}); +} + +.#{$fa-css-prefix}-stack-1x { + line-height: inherit; +} + +.#{$fa-css-prefix}-stack-2x { + font-size: 2em; +} + +.#{$fa-css-prefix}-inverse { + color: var(--#{$fa-css-prefix}-inverse, #{$fa-inverse}); +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_variables.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_variables.scss new file mode 100644 index 0000000..858541f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/_variables.scss @@ -0,0 +1,4961 @@ +// variables +// -------------------------- + +$fa-css-prefix : fa !default; +$fa-style : 900 !default; +$fa-style-family : "Font Awesome 6 Free" !default; + +$fa-display : inline-block !default; + +$fa-fw-width : fa-divide(20em, 16) !default; +$fa-inverse : #fff !default; + +$fa-border-color : #eee !default; +$fa-border-padding : .2em .25em .15em !default; +$fa-border-radius : .1em !default; +$fa-border-style : solid !default; +$fa-border-width : .08em !default; + +$fa-size-scale-2xs : 10 !default; +$fa-size-scale-xs : 12 !default; +$fa-size-scale-sm : 14 !default; +$fa-size-scale-base : 16 !default; +$fa-size-scale-lg : 20 !default; +$fa-size-scale-xl : 24 !default; +$fa-size-scale-2xl : 32 !default; + +$fa-sizes: ( + "2xs" : $fa-size-scale-2xs, + "xs" : $fa-size-scale-xs, + "sm" : $fa-size-scale-sm, + "lg" : $fa-size-scale-lg, + "xl" : $fa-size-scale-xl, + "2xl" : $fa-size-scale-2xl +) !default; + +$fa-li-width : 2em !default; +$fa-li-margin : $fa-li-width * fa-divide(5, 4) !default; + +$fa-pull-margin : .3em !default; + +$fa-primary-opacity : 1 !default; +$fa-secondary-opacity : .4 !default; + +$fa-stack-vertical-align: middle !default; +$fa-stack-width : ($fa-fw-width * 2) !default; +$fa-stack-z-index : auto !default; + +$fa-font-display : block !default; +$fa-font-path : "/fonts/fontawesome-free" !default; + +$fa-var-0: \30; +$fa-var-1: \31; +$fa-var-2: \32; +$fa-var-3: \33; +$fa-var-4: \34; +$fa-var-5: \35; +$fa-var-6: \36; +$fa-var-7: \37; +$fa-var-8: \38; +$fa-var-9: \39; +$fa-var-fill-drip: \f576; +$fa-var-arrows-to-circle: \e4bd; +$fa-var-circle-chevron-right: \f138; +$fa-var-chevron-circle-right: \f138; +$fa-var-at: \40; +$fa-var-trash-can: \f2ed; +$fa-var-trash-alt: \f2ed; +$fa-var-text-height: \f034; +$fa-var-user-xmark: \f235; +$fa-var-user-times: \f235; +$fa-var-stethoscope: \f0f1; +$fa-var-message: \f27a; +$fa-var-comment-alt: \f27a; +$fa-var-info: \f129; +$fa-var-down-left-and-up-right-to-center: \f422; +$fa-var-compress-alt: \f422; +$fa-var-explosion: \e4e9; +$fa-var-file-lines: \f15c; +$fa-var-file-alt: \f15c; +$fa-var-file-text: \f15c; +$fa-var-wave-square: \f83e; +$fa-var-ring: \f70b; +$fa-var-building-un: \e4d9; +$fa-var-dice-three: \f527; +$fa-var-calendar-days: \f073; +$fa-var-calendar-alt: \f073; +$fa-var-anchor-circle-check: \e4aa; +$fa-var-building-circle-arrow-right: \e4d1; +$fa-var-volleyball: \f45f; +$fa-var-volleyball-ball: \f45f; +$fa-var-arrows-up-to-line: \e4c2; +$fa-var-sort-down: \f0dd; +$fa-var-sort-desc: \f0dd; +$fa-var-circle-minus: \f056; +$fa-var-minus-circle: \f056; +$fa-var-door-open: \f52b; +$fa-var-right-from-bracket: \f2f5; +$fa-var-sign-out-alt: \f2f5; +$fa-var-atom: \f5d2; +$fa-var-soap: \e06e; +$fa-var-icons: \f86d; +$fa-var-heart-music-camera-bolt: \f86d; +$fa-var-microphone-lines-slash: \f539; +$fa-var-microphone-alt-slash: \f539; +$fa-var-bridge-circle-check: \e4c9; +$fa-var-pump-medical: \e06a; +$fa-var-fingerprint: \f577; +$fa-var-hand-point-right: \f0a4; +$fa-var-magnifying-glass-location: \f689; +$fa-var-search-location: \f689; +$fa-var-forward-step: \f051; +$fa-var-step-forward: \f051; +$fa-var-face-smile-beam: \f5b8; +$fa-var-smile-beam: \f5b8; +$fa-var-flag-checkered: \f11e; +$fa-var-football: \f44e; +$fa-var-football-ball: \f44e; +$fa-var-school-circle-exclamation: \e56c; +$fa-var-crop: \f125; +$fa-var-angles-down: \f103; +$fa-var-angle-double-down: \f103; +$fa-var-users-rectangle: \e594; +$fa-var-people-roof: \e537; +$fa-var-people-line: \e534; +$fa-var-beer-mug-empty: \f0fc; +$fa-var-beer: \f0fc; +$fa-var-diagram-predecessor: \e477; +$fa-var-arrow-up-long: \f176; +$fa-var-long-arrow-up: \f176; +$fa-var-fire-flame-simple: \f46a; +$fa-var-burn: \f46a; +$fa-var-person: \f183; +$fa-var-male: \f183; +$fa-var-laptop: \f109; +$fa-var-file-csv: \f6dd; +$fa-var-menorah: \f676; +$fa-var-truck-plane: \e58f; +$fa-var-record-vinyl: \f8d9; +$fa-var-face-grin-stars: \f587; +$fa-var-grin-stars: \f587; +$fa-var-bong: \f55c; +$fa-var-spaghetti-monster-flying: \f67b; +$fa-var-pastafarianism: \f67b; +$fa-var-arrow-down-up-across-line: \e4af; +$fa-var-spoon: \f2e5; +$fa-var-utensil-spoon: \f2e5; +$fa-var-jar-wheat: \e517; +$fa-var-envelopes-bulk: \f674; +$fa-var-mail-bulk: \f674; +$fa-var-file-circle-exclamation: \e4eb; +$fa-var-circle-h: \f47e; +$fa-var-hospital-symbol: \f47e; +$fa-var-pager: \f815; +$fa-var-address-book: \f2b9; +$fa-var-contact-book: \f2b9; +$fa-var-strikethrough: \f0cc; +$fa-var-k: \4b; +$fa-var-landmark-flag: \e51c; +$fa-var-pencil: \f303; +$fa-var-pencil-alt: \f303; +$fa-var-backward: \f04a; +$fa-var-caret-right: \f0da; +$fa-var-comments: \f086; +$fa-var-paste: \f0ea; +$fa-var-file-clipboard: \f0ea; +$fa-var-code-pull-request: \e13c; +$fa-var-clipboard-list: \f46d; +$fa-var-truck-ramp-box: \f4de; +$fa-var-truck-loading: \f4de; +$fa-var-user-check: \f4fc; +$fa-var-vial-virus: \e597; +$fa-var-sheet-plastic: \e571; +$fa-var-blog: \f781; +$fa-var-user-ninja: \f504; +$fa-var-person-arrow-up-from-line: \e539; +$fa-var-scroll-torah: \f6a0; +$fa-var-torah: \f6a0; +$fa-var-broom-ball: \f458; +$fa-var-quidditch: \f458; +$fa-var-quidditch-broom-ball: \f458; +$fa-var-toggle-off: \f204; +$fa-var-box-archive: \f187; +$fa-var-archive: \f187; +$fa-var-person-drowning: \e545; +$fa-var-arrow-down-9-1: \f886; +$fa-var-sort-numeric-desc: \f886; +$fa-var-sort-numeric-down-alt: \f886; +$fa-var-face-grin-tongue-squint: \f58a; +$fa-var-grin-tongue-squint: \f58a; +$fa-var-spray-can: \f5bd; +$fa-var-truck-monster: \f63b; +$fa-var-w: \57; +$fa-var-earth-africa: \f57c; +$fa-var-globe-africa: \f57c; +$fa-var-rainbow: \f75b; +$fa-var-circle-notch: \f1ce; +$fa-var-tablet-screen-button: \f3fa; +$fa-var-tablet-alt: \f3fa; +$fa-var-paw: \f1b0; +$fa-var-cloud: \f0c2; +$fa-var-trowel-bricks: \e58a; +$fa-var-face-flushed: \f579; +$fa-var-flushed: \f579; +$fa-var-hospital-user: \f80d; +$fa-var-tent-arrow-left-right: \e57f; +$fa-var-gavel: \f0e3; +$fa-var-legal: \f0e3; +$fa-var-binoculars: \f1e5; +$fa-var-microphone-slash: \f131; +$fa-var-box-tissue: \e05b; +$fa-var-motorcycle: \f21c; +$fa-var-bell-concierge: \f562; +$fa-var-concierge-bell: \f562; +$fa-var-pen-ruler: \f5ae; +$fa-var-pencil-ruler: \f5ae; +$fa-var-people-arrows: \e068; +$fa-var-people-arrows-left-right: \e068; +$fa-var-mars-and-venus-burst: \e523; +$fa-var-square-caret-right: \f152; +$fa-var-caret-square-right: \f152; +$fa-var-scissors: \f0c4; +$fa-var-cut: \f0c4; +$fa-var-sun-plant-wilt: \e57a; +$fa-var-toilets-portable: \e584; +$fa-var-hockey-puck: \f453; +$fa-var-table: \f0ce; +$fa-var-magnifying-glass-arrow-right: \e521; +$fa-var-tachograph-digital: \f566; +$fa-var-digital-tachograph: \f566; +$fa-var-users-slash: \e073; +$fa-var-clover: \e139; +$fa-var-reply: \f3e5; +$fa-var-mail-reply: \f3e5; +$fa-var-star-and-crescent: \f699; +$fa-var-house-fire: \e50c; +$fa-var-square-minus: \f146; +$fa-var-minus-square: \f146; +$fa-var-helicopter: \f533; +$fa-var-compass: \f14e; +$fa-var-square-caret-down: \f150; +$fa-var-caret-square-down: \f150; +$fa-var-file-circle-question: \e4ef; +$fa-var-laptop-code: \f5fc; +$fa-var-swatchbook: \f5c3; +$fa-var-prescription-bottle: \f485; +$fa-var-bars: \f0c9; +$fa-var-navicon: \f0c9; +$fa-var-people-group: \e533; +$fa-var-hourglass-end: \f253; +$fa-var-hourglass-3: \f253; +$fa-var-heart-crack: \f7a9; +$fa-var-heart-broken: \f7a9; +$fa-var-square-up-right: \f360; +$fa-var-external-link-square-alt: \f360; +$fa-var-face-kiss-beam: \f597; +$fa-var-kiss-beam: \f597; +$fa-var-film: \f008; +$fa-var-ruler-horizontal: \f547; +$fa-var-people-robbery: \e536; +$fa-var-lightbulb: \f0eb; +$fa-var-caret-left: \f0d9; +$fa-var-circle-exclamation: \f06a; +$fa-var-exclamation-circle: \f06a; +$fa-var-school-circle-xmark: \e56d; +$fa-var-arrow-right-from-bracket: \f08b; +$fa-var-sign-out: \f08b; +$fa-var-circle-chevron-down: \f13a; +$fa-var-chevron-circle-down: \f13a; +$fa-var-unlock-keyhole: \f13e; +$fa-var-unlock-alt: \f13e; +$fa-var-cloud-showers-heavy: \f740; +$fa-var-headphones-simple: \f58f; +$fa-var-headphones-alt: \f58f; +$fa-var-sitemap: \f0e8; +$fa-var-circle-dollar-to-slot: \f4b9; +$fa-var-donate: \f4b9; +$fa-var-memory: \f538; +$fa-var-road-spikes: \e568; +$fa-var-fire-burner: \e4f1; +$fa-var-flag: \f024; +$fa-var-hanukiah: \f6e6; +$fa-var-feather: \f52d; +$fa-var-volume-low: \f027; +$fa-var-volume-down: \f027; +$fa-var-comment-slash: \f4b3; +$fa-var-cloud-sun-rain: \f743; +$fa-var-compress: \f066; +$fa-var-wheat-awn: \e2cd; +$fa-var-wheat-alt: \e2cd; +$fa-var-ankh: \f644; +$fa-var-hands-holding-child: \e4fa; +$fa-var-asterisk: \2a; +$fa-var-square-check: \f14a; +$fa-var-check-square: \f14a; +$fa-var-peseta-sign: \e221; +$fa-var-heading: \f1dc; +$fa-var-header: \f1dc; +$fa-var-ghost: \f6e2; +$fa-var-list: \f03a; +$fa-var-list-squares: \f03a; +$fa-var-square-phone-flip: \f87b; +$fa-var-phone-square-alt: \f87b; +$fa-var-cart-plus: \f217; +$fa-var-gamepad: \f11b; +$fa-var-circle-dot: \f192; +$fa-var-dot-circle: \f192; +$fa-var-face-dizzy: \f567; +$fa-var-dizzy: \f567; +$fa-var-egg: \f7fb; +$fa-var-house-medical-circle-xmark: \e513; +$fa-var-campground: \f6bb; +$fa-var-folder-plus: \f65e; +$fa-var-futbol: \f1e3; +$fa-var-futbol-ball: \f1e3; +$fa-var-soccer-ball: \f1e3; +$fa-var-paintbrush: \f1fc; +$fa-var-paint-brush: \f1fc; +$fa-var-lock: \f023; +$fa-var-gas-pump: \f52f; +$fa-var-hot-tub-person: \f593; +$fa-var-hot-tub: \f593; +$fa-var-map-location: \f59f; +$fa-var-map-marked: \f59f; +$fa-var-house-flood-water: \e50e; +$fa-var-tree: \f1bb; +$fa-var-bridge-lock: \e4cc; +$fa-var-sack-dollar: \f81d; +$fa-var-pen-to-square: \f044; +$fa-var-edit: \f044; +$fa-var-car-side: \f5e4; +$fa-var-share-nodes: \f1e0; +$fa-var-share-alt: \f1e0; +$fa-var-heart-circle-minus: \e4ff; +$fa-var-hourglass-half: \f252; +$fa-var-hourglass-2: \f252; +$fa-var-microscope: \f610; +$fa-var-sink: \e06d; +$fa-var-bag-shopping: \f290; +$fa-var-shopping-bag: \f290; +$fa-var-arrow-down-z-a: \f881; +$fa-var-sort-alpha-desc: \f881; +$fa-var-sort-alpha-down-alt: \f881; +$fa-var-mitten: \f7b5; +$fa-var-person-rays: \e54d; +$fa-var-users: \f0c0; +$fa-var-eye-slash: \f070; +$fa-var-flask-vial: \e4f3; +$fa-var-hand: \f256; +$fa-var-hand-paper: \f256; +$fa-var-om: \f679; +$fa-var-worm: \e599; +$fa-var-house-circle-xmark: \e50b; +$fa-var-plug: \f1e6; +$fa-var-chevron-up: \f077; +$fa-var-hand-spock: \f259; +$fa-var-stopwatch: \f2f2; +$fa-var-face-kiss: \f596; +$fa-var-kiss: \f596; +$fa-var-bridge-circle-xmark: \e4cb; +$fa-var-face-grin-tongue: \f589; +$fa-var-grin-tongue: \f589; +$fa-var-chess-bishop: \f43a; +$fa-var-face-grin-wink: \f58c; +$fa-var-grin-wink: \f58c; +$fa-var-ear-deaf: \f2a4; +$fa-var-deaf: \f2a4; +$fa-var-deafness: \f2a4; +$fa-var-hard-of-hearing: \f2a4; +$fa-var-road-circle-check: \e564; +$fa-var-dice-five: \f523; +$fa-var-square-rss: \f143; +$fa-var-rss-square: \f143; +$fa-var-land-mine-on: \e51b; +$fa-var-i-cursor: \f246; +$fa-var-stamp: \f5bf; +$fa-var-stairs: \e289; +$fa-var-i: \49; +$fa-var-hryvnia-sign: \f6f2; +$fa-var-hryvnia: \f6f2; +$fa-var-pills: \f484; +$fa-var-face-grin-wide: \f581; +$fa-var-grin-alt: \f581; +$fa-var-tooth: \f5c9; +$fa-var-v: \56; +$fa-var-bangladeshi-taka-sign: \e2e6; +$fa-var-bicycle: \f206; +$fa-var-staff-snake: \e579; +$fa-var-rod-asclepius: \e579; +$fa-var-rod-snake: \e579; +$fa-var-staff-aesculapius: \e579; +$fa-var-head-side-cough-slash: \e062; +$fa-var-truck-medical: \f0f9; +$fa-var-ambulance: \f0f9; +$fa-var-wheat-awn-circle-exclamation: \e598; +$fa-var-snowman: \f7d0; +$fa-var-mortar-pestle: \f5a7; +$fa-var-road-barrier: \e562; +$fa-var-school: \f549; +$fa-var-igloo: \f7ae; +$fa-var-joint: \f595; +$fa-var-angle-right: \f105; +$fa-var-horse: \f6f0; +$fa-var-q: \51; +$fa-var-g: \47; +$fa-var-notes-medical: \f481; +$fa-var-temperature-half: \f2c9; +$fa-var-temperature-2: \f2c9; +$fa-var-thermometer-2: \f2c9; +$fa-var-thermometer-half: \f2c9; +$fa-var-dong-sign: \e169; +$fa-var-capsules: \f46b; +$fa-var-poo-storm: \f75a; +$fa-var-poo-bolt: \f75a; +$fa-var-face-frown-open: \f57a; +$fa-var-frown-open: \f57a; +$fa-var-hand-point-up: \f0a6; +$fa-var-money-bill: \f0d6; +$fa-var-bookmark: \f02e; +$fa-var-align-justify: \f039; +$fa-var-umbrella-beach: \f5ca; +$fa-var-helmet-un: \e503; +$fa-var-bullseye: \f140; +$fa-var-bacon: \f7e5; +$fa-var-hand-point-down: \f0a7; +$fa-var-arrow-up-from-bracket: \e09a; +$fa-var-folder: \f07b; +$fa-var-folder-blank: \f07b; +$fa-var-file-waveform: \f478; +$fa-var-file-medical-alt: \f478; +$fa-var-radiation: \f7b9; +$fa-var-chart-simple: \e473; +$fa-var-mars-stroke: \f229; +$fa-var-vial: \f492; +$fa-var-gauge: \f624; +$fa-var-dashboard: \f624; +$fa-var-gauge-med: \f624; +$fa-var-tachometer-alt-average: \f624; +$fa-var-wand-magic-sparkles: \e2ca; +$fa-var-magic-wand-sparkles: \e2ca; +$fa-var-e: \45; +$fa-var-pen-clip: \f305; +$fa-var-pen-alt: \f305; +$fa-var-bridge-circle-exclamation: \e4ca; +$fa-var-user: \f007; +$fa-var-school-circle-check: \e56b; +$fa-var-dumpster: \f793; +$fa-var-van-shuttle: \f5b6; +$fa-var-shuttle-van: \f5b6; +$fa-var-building-user: \e4da; +$fa-var-square-caret-left: \f191; +$fa-var-caret-square-left: \f191; +$fa-var-highlighter: \f591; +$fa-var-key: \f084; +$fa-var-bullhorn: \f0a1; +$fa-var-globe: \f0ac; +$fa-var-synagogue: \f69b; +$fa-var-person-half-dress: \e548; +$fa-var-road-bridge: \e563; +$fa-var-location-arrow: \f124; +$fa-var-c: \43; +$fa-var-tablet-button: \f10a; +$fa-var-building-lock: \e4d6; +$fa-var-pizza-slice: \f818; +$fa-var-money-bill-wave: \f53a; +$fa-var-chart-area: \f1fe; +$fa-var-area-chart: \f1fe; +$fa-var-house-flag: \e50d; +$fa-var-person-circle-minus: \e540; +$fa-var-ban: \f05e; +$fa-var-cancel: \f05e; +$fa-var-camera-rotate: \e0d8; +$fa-var-spray-can-sparkles: \f5d0; +$fa-var-air-freshener: \f5d0; +$fa-var-star: \f005; +$fa-var-repeat: \f363; +$fa-var-cross: \f654; +$fa-var-box: \f466; +$fa-var-venus-mars: \f228; +$fa-var-arrow-pointer: \f245; +$fa-var-mouse-pointer: \f245; +$fa-var-maximize: \f31e; +$fa-var-expand-arrows-alt: \f31e; +$fa-var-charging-station: \f5e7; +$fa-var-shapes: \f61f; +$fa-var-triangle-circle-square: \f61f; +$fa-var-shuffle: \f074; +$fa-var-random: \f074; +$fa-var-person-running: \f70c; +$fa-var-running: \f70c; +$fa-var-mobile-retro: \e527; +$fa-var-grip-lines-vertical: \f7a5; +$fa-var-spider: \f717; +$fa-var-hands-bound: \e4f9; +$fa-var-file-invoice-dollar: \f571; +$fa-var-plane-circle-exclamation: \e556; +$fa-var-x-ray: \f497; +$fa-var-spell-check: \f891; +$fa-var-slash: \f715; +$fa-var-computer-mouse: \f8cc; +$fa-var-mouse: \f8cc; +$fa-var-arrow-right-to-bracket: \f090; +$fa-var-sign-in: \f090; +$fa-var-shop-slash: \e070; +$fa-var-store-alt-slash: \e070; +$fa-var-server: \f233; +$fa-var-virus-covid-slash: \e4a9; +$fa-var-shop-lock: \e4a5; +$fa-var-hourglass-start: \f251; +$fa-var-hourglass-1: \f251; +$fa-var-blender-phone: \f6b6; +$fa-var-building-wheat: \e4db; +$fa-var-person-breastfeeding: \e53a; +$fa-var-right-to-bracket: \f2f6; +$fa-var-sign-in-alt: \f2f6; +$fa-var-venus: \f221; +$fa-var-passport: \f5ab; +$fa-var-heart-pulse: \f21e; +$fa-var-heartbeat: \f21e; +$fa-var-people-carry-box: \f4ce; +$fa-var-people-carry: \f4ce; +$fa-var-temperature-high: \f769; +$fa-var-microchip: \f2db; +$fa-var-crown: \f521; +$fa-var-weight-hanging: \f5cd; +$fa-var-xmarks-lines: \e59a; +$fa-var-file-prescription: \f572; +$fa-var-weight-scale: \f496; +$fa-var-weight: \f496; +$fa-var-user-group: \f500; +$fa-var-user-friends: \f500; +$fa-var-arrow-up-a-z: \f15e; +$fa-var-sort-alpha-up: \f15e; +$fa-var-chess-knight: \f441; +$fa-var-face-laugh-squint: \f59b; +$fa-var-laugh-squint: \f59b; +$fa-var-wheelchair: \f193; +$fa-var-circle-arrow-up: \f0aa; +$fa-var-arrow-circle-up: \f0aa; +$fa-var-toggle-on: \f205; +$fa-var-person-walking: \f554; +$fa-var-walking: \f554; +$fa-var-l: \4c; +$fa-var-fire: \f06d; +$fa-var-bed-pulse: \f487; +$fa-var-procedures: \f487; +$fa-var-shuttle-space: \f197; +$fa-var-space-shuttle: \f197; +$fa-var-face-laugh: \f599; +$fa-var-laugh: \f599; +$fa-var-folder-open: \f07c; +$fa-var-heart-circle-plus: \e500; +$fa-var-code-fork: \e13b; +$fa-var-city: \f64f; +$fa-var-microphone-lines: \f3c9; +$fa-var-microphone-alt: \f3c9; +$fa-var-pepper-hot: \f816; +$fa-var-unlock: \f09c; +$fa-var-colon-sign: \e140; +$fa-var-headset: \f590; +$fa-var-store-slash: \e071; +$fa-var-road-circle-xmark: \e566; +$fa-var-user-minus: \f503; +$fa-var-mars-stroke-up: \f22a; +$fa-var-mars-stroke-v: \f22a; +$fa-var-champagne-glasses: \f79f; +$fa-var-glass-cheers: \f79f; +$fa-var-clipboard: \f328; +$fa-var-house-circle-exclamation: \e50a; +$fa-var-file-arrow-up: \f574; +$fa-var-file-upload: \f574; +$fa-var-wifi: \f1eb; +$fa-var-wifi-3: \f1eb; +$fa-var-wifi-strong: \f1eb; +$fa-var-bath: \f2cd; +$fa-var-bathtub: \f2cd; +$fa-var-underline: \f0cd; +$fa-var-user-pen: \f4ff; +$fa-var-user-edit: \f4ff; +$fa-var-signature: \f5b7; +$fa-var-stroopwafel: \f551; +$fa-var-bold: \f032; +$fa-var-anchor-lock: \e4ad; +$fa-var-building-ngo: \e4d7; +$fa-var-manat-sign: \e1d5; +$fa-var-not-equal: \f53e; +$fa-var-border-top-left: \f853; +$fa-var-border-style: \f853; +$fa-var-map-location-dot: \f5a0; +$fa-var-map-marked-alt: \f5a0; +$fa-var-jedi: \f669; +$fa-var-square-poll-vertical: \f681; +$fa-var-poll: \f681; +$fa-var-mug-hot: \f7b6; +$fa-var-car-battery: \f5df; +$fa-var-battery-car: \f5df; +$fa-var-gift: \f06b; +$fa-var-dice-two: \f528; +$fa-var-chess-queen: \f445; +$fa-var-glasses: \f530; +$fa-var-chess-board: \f43c; +$fa-var-building-circle-check: \e4d2; +$fa-var-person-chalkboard: \e53d; +$fa-var-mars-stroke-right: \f22b; +$fa-var-mars-stroke-h: \f22b; +$fa-var-hand-back-fist: \f255; +$fa-var-hand-rock: \f255; +$fa-var-square-caret-up: \f151; +$fa-var-caret-square-up: \f151; +$fa-var-cloud-showers-water: \e4e4; +$fa-var-chart-bar: \f080; +$fa-var-bar-chart: \f080; +$fa-var-hands-bubbles: \e05e; +$fa-var-hands-wash: \e05e; +$fa-var-less-than-equal: \f537; +$fa-var-train: \f238; +$fa-var-eye-low-vision: \f2a8; +$fa-var-low-vision: \f2a8; +$fa-var-crow: \f520; +$fa-var-sailboat: \e445; +$fa-var-window-restore: \f2d2; +$fa-var-square-plus: \f0fe; +$fa-var-plus-square: \f0fe; +$fa-var-torii-gate: \f6a1; +$fa-var-frog: \f52e; +$fa-var-bucket: \e4cf; +$fa-var-image: \f03e; +$fa-var-microphone: \f130; +$fa-var-cow: \f6c8; +$fa-var-caret-up: \f0d8; +$fa-var-screwdriver: \f54a; +$fa-var-folder-closed: \e185; +$fa-var-house-tsunami: \e515; +$fa-var-square-nfi: \e576; +$fa-var-arrow-up-from-ground-water: \e4b5; +$fa-var-martini-glass: \f57b; +$fa-var-glass-martini-alt: \f57b; +$fa-var-rotate-left: \f2ea; +$fa-var-rotate-back: \f2ea; +$fa-var-rotate-backward: \f2ea; +$fa-var-undo-alt: \f2ea; +$fa-var-table-columns: \f0db; +$fa-var-columns: \f0db; +$fa-var-lemon: \f094; +$fa-var-head-side-mask: \e063; +$fa-var-handshake: \f2b5; +$fa-var-gem: \f3a5; +$fa-var-dolly: \f472; +$fa-var-dolly-box: \f472; +$fa-var-smoking: \f48d; +$fa-var-minimize: \f78c; +$fa-var-compress-arrows-alt: \f78c; +$fa-var-monument: \f5a6; +$fa-var-snowplow: \f7d2; +$fa-var-angles-right: \f101; +$fa-var-angle-double-right: \f101; +$fa-var-cannabis: \f55f; +$fa-var-circle-play: \f144; +$fa-var-play-circle: \f144; +$fa-var-tablets: \f490; +$fa-var-ethernet: \f796; +$fa-var-euro-sign: \f153; +$fa-var-eur: \f153; +$fa-var-euro: \f153; +$fa-var-chair: \f6c0; +$fa-var-circle-check: \f058; +$fa-var-check-circle: \f058; +$fa-var-circle-stop: \f28d; +$fa-var-stop-circle: \f28d; +$fa-var-compass-drafting: \f568; +$fa-var-drafting-compass: \f568; +$fa-var-plate-wheat: \e55a; +$fa-var-icicles: \f7ad; +$fa-var-person-shelter: \e54f; +$fa-var-neuter: \f22c; +$fa-var-id-badge: \f2c1; +$fa-var-marker: \f5a1; +$fa-var-face-laugh-beam: \f59a; +$fa-var-laugh-beam: \f59a; +$fa-var-helicopter-symbol: \e502; +$fa-var-universal-access: \f29a; +$fa-var-circle-chevron-up: \f139; +$fa-var-chevron-circle-up: \f139; +$fa-var-lari-sign: \e1c8; +$fa-var-volcano: \f770; +$fa-var-person-walking-dashed-line-arrow-right: \e553; +$fa-var-sterling-sign: \f154; +$fa-var-gbp: \f154; +$fa-var-pound-sign: \f154; +$fa-var-viruses: \e076; +$fa-var-square-person-confined: \e577; +$fa-var-user-tie: \f508; +$fa-var-arrow-down-long: \f175; +$fa-var-long-arrow-down: \f175; +$fa-var-tent-arrow-down-to-line: \e57e; +$fa-var-certificate: \f0a3; +$fa-var-reply-all: \f122; +$fa-var-mail-reply-all: \f122; +$fa-var-suitcase: \f0f2; +$fa-var-person-skating: \f7c5; +$fa-var-skating: \f7c5; +$fa-var-filter-circle-dollar: \f662; +$fa-var-funnel-dollar: \f662; +$fa-var-camera-retro: \f083; +$fa-var-circle-arrow-down: \f0ab; +$fa-var-arrow-circle-down: \f0ab; +$fa-var-file-import: \f56f; +$fa-var-arrow-right-to-file: \f56f; +$fa-var-square-arrow-up-right: \f14c; +$fa-var-external-link-square: \f14c; +$fa-var-box-open: \f49e; +$fa-var-scroll: \f70e; +$fa-var-spa: \f5bb; +$fa-var-location-pin-lock: \e51f; +$fa-var-pause: \f04c; +$fa-var-hill-avalanche: \e507; +$fa-var-temperature-empty: \f2cb; +$fa-var-temperature-0: \f2cb; +$fa-var-thermometer-0: \f2cb; +$fa-var-thermometer-empty: \f2cb; +$fa-var-bomb: \f1e2; +$fa-var-registered: \f25d; +$fa-var-address-card: \f2bb; +$fa-var-contact-card: \f2bb; +$fa-var-vcard: \f2bb; +$fa-var-scale-unbalanced-flip: \f516; +$fa-var-balance-scale-right: \f516; +$fa-var-subscript: \f12c; +$fa-var-diamond-turn-right: \f5eb; +$fa-var-directions: \f5eb; +$fa-var-burst: \e4dc; +$fa-var-house-laptop: \e066; +$fa-var-laptop-house: \e066; +$fa-var-face-tired: \f5c8; +$fa-var-tired: \f5c8; +$fa-var-money-bills: \e1f3; +$fa-var-smog: \f75f; +$fa-var-crutch: \f7f7; +$fa-var-cloud-arrow-up: \f0ee; +$fa-var-cloud-upload: \f0ee; +$fa-var-cloud-upload-alt: \f0ee; +$fa-var-palette: \f53f; +$fa-var-arrows-turn-right: \e4c0; +$fa-var-vest: \e085; +$fa-var-ferry: \e4ea; +$fa-var-arrows-down-to-people: \e4b9; +$fa-var-seedling: \f4d8; +$fa-var-sprout: \f4d8; +$fa-var-left-right: \f337; +$fa-var-arrows-alt-h: \f337; +$fa-var-boxes-packing: \e4c7; +$fa-var-circle-arrow-left: \f0a8; +$fa-var-arrow-circle-left: \f0a8; +$fa-var-group-arrows-rotate: \e4f6; +$fa-var-bowl-food: \e4c6; +$fa-var-candy-cane: \f786; +$fa-var-arrow-down-wide-short: \f160; +$fa-var-sort-amount-asc: \f160; +$fa-var-sort-amount-down: \f160; +$fa-var-cloud-bolt: \f76c; +$fa-var-thunderstorm: \f76c; +$fa-var-text-slash: \f87d; +$fa-var-remove-format: \f87d; +$fa-var-face-smile-wink: \f4da; +$fa-var-smile-wink: \f4da; +$fa-var-file-word: \f1c2; +$fa-var-file-powerpoint: \f1c4; +$fa-var-arrows-left-right: \f07e; +$fa-var-arrows-h: \f07e; +$fa-var-house-lock: \e510; +$fa-var-cloud-arrow-down: \f0ed; +$fa-var-cloud-download: \f0ed; +$fa-var-cloud-download-alt: \f0ed; +$fa-var-children: \e4e1; +$fa-var-chalkboard: \f51b; +$fa-var-blackboard: \f51b; +$fa-var-user-large-slash: \f4fa; +$fa-var-user-alt-slash: \f4fa; +$fa-var-envelope-open: \f2b6; +$fa-var-handshake-simple-slash: \e05f; +$fa-var-handshake-alt-slash: \e05f; +$fa-var-mattress-pillow: \e525; +$fa-var-guarani-sign: \e19a; +$fa-var-arrows-rotate: \f021; +$fa-var-refresh: \f021; +$fa-var-sync: \f021; +$fa-var-fire-extinguisher: \f134; +$fa-var-cruzeiro-sign: \e152; +$fa-var-greater-than-equal: \f532; +$fa-var-shield-halved: \f3ed; +$fa-var-shield-alt: \f3ed; +$fa-var-book-atlas: \f558; +$fa-var-atlas: \f558; +$fa-var-virus: \e074; +$fa-var-envelope-circle-check: \e4e8; +$fa-var-layer-group: \f5fd; +$fa-var-arrows-to-dot: \e4be; +$fa-var-archway: \f557; +$fa-var-heart-circle-check: \e4fd; +$fa-var-house-chimney-crack: \f6f1; +$fa-var-house-damage: \f6f1; +$fa-var-file-zipper: \f1c6; +$fa-var-file-archive: \f1c6; +$fa-var-square: \f0c8; +$fa-var-martini-glass-empty: \f000; +$fa-var-glass-martini: \f000; +$fa-var-couch: \f4b8; +$fa-var-cedi-sign: \e0df; +$fa-var-italic: \f033; +$fa-var-church: \f51d; +$fa-var-comments-dollar: \f653; +$fa-var-democrat: \f747; +$fa-var-z: \5a; +$fa-var-person-skiing: \f7c9; +$fa-var-skiing: \f7c9; +$fa-var-road-lock: \e567; +$fa-var-a: \41; +$fa-var-temperature-arrow-down: \e03f; +$fa-var-temperature-down: \e03f; +$fa-var-feather-pointed: \f56b; +$fa-var-feather-alt: \f56b; +$fa-var-p: \50; +$fa-var-snowflake: \f2dc; +$fa-var-newspaper: \f1ea; +$fa-var-rectangle-ad: \f641; +$fa-var-ad: \f641; +$fa-var-circle-arrow-right: \f0a9; +$fa-var-arrow-circle-right: \f0a9; +$fa-var-filter-circle-xmark: \e17b; +$fa-var-locust: \e520; +$fa-var-sort: \f0dc; +$fa-var-unsorted: \f0dc; +$fa-var-list-ol: \f0cb; +$fa-var-list-1-2: \f0cb; +$fa-var-list-numeric: \f0cb; +$fa-var-person-dress-burst: \e544; +$fa-var-money-check-dollar: \f53d; +$fa-var-money-check-alt: \f53d; +$fa-var-vector-square: \f5cb; +$fa-var-bread-slice: \f7ec; +$fa-var-language: \f1ab; +$fa-var-face-kiss-wink-heart: \f598; +$fa-var-kiss-wink-heart: \f598; +$fa-var-filter: \f0b0; +$fa-var-question: \3f; +$fa-var-file-signature: \f573; +$fa-var-up-down-left-right: \f0b2; +$fa-var-arrows-alt: \f0b2; +$fa-var-house-chimney-user: \e065; +$fa-var-hand-holding-heart: \f4be; +$fa-var-puzzle-piece: \f12e; +$fa-var-money-check: \f53c; +$fa-var-star-half-stroke: \f5c0; +$fa-var-star-half-alt: \f5c0; +$fa-var-code: \f121; +$fa-var-whiskey-glass: \f7a0; +$fa-var-glass-whiskey: \f7a0; +$fa-var-building-circle-exclamation: \e4d3; +$fa-var-magnifying-glass-chart: \e522; +$fa-var-arrow-up-right-from-square: \f08e; +$fa-var-external-link: \f08e; +$fa-var-cubes-stacked: \e4e6; +$fa-var-won-sign: \f159; +$fa-var-krw: \f159; +$fa-var-won: \f159; +$fa-var-virus-covid: \e4a8; +$fa-var-austral-sign: \e0a9; +$fa-var-f: \46; +$fa-var-leaf: \f06c; +$fa-var-road: \f018; +$fa-var-taxi: \f1ba; +$fa-var-cab: \f1ba; +$fa-var-person-circle-plus: \e541; +$fa-var-chart-pie: \f200; +$fa-var-pie-chart: \f200; +$fa-var-bolt-lightning: \e0b7; +$fa-var-sack-xmark: \e56a; +$fa-var-file-excel: \f1c3; +$fa-var-file-contract: \f56c; +$fa-var-fish-fins: \e4f2; +$fa-var-building-flag: \e4d5; +$fa-var-face-grin-beam: \f582; +$fa-var-grin-beam: \f582; +$fa-var-object-ungroup: \f248; +$fa-var-poop: \f619; +$fa-var-location-pin: \f041; +$fa-var-map-marker: \f041; +$fa-var-kaaba: \f66b; +$fa-var-toilet-paper: \f71e; +$fa-var-helmet-safety: \f807; +$fa-var-hard-hat: \f807; +$fa-var-hat-hard: \f807; +$fa-var-eject: \f052; +$fa-var-circle-right: \f35a; +$fa-var-arrow-alt-circle-right: \f35a; +$fa-var-plane-circle-check: \e555; +$fa-var-face-rolling-eyes: \f5a5; +$fa-var-meh-rolling-eyes: \f5a5; +$fa-var-object-group: \f247; +$fa-var-chart-line: \f201; +$fa-var-line-chart: \f201; +$fa-var-mask-ventilator: \e524; +$fa-var-arrow-right: \f061; +$fa-var-signs-post: \f277; +$fa-var-map-signs: \f277; +$fa-var-cash-register: \f788; +$fa-var-person-circle-question: \e542; +$fa-var-h: \48; +$fa-var-tarp: \e57b; +$fa-var-screwdriver-wrench: \f7d9; +$fa-var-tools: \f7d9; +$fa-var-arrows-to-eye: \e4bf; +$fa-var-plug-circle-bolt: \e55b; +$fa-var-heart: \f004; +$fa-var-mars-and-venus: \f224; +$fa-var-house-user: \e1b0; +$fa-var-home-user: \e1b0; +$fa-var-dumpster-fire: \f794; +$fa-var-house-crack: \e3b1; +$fa-var-martini-glass-citrus: \f561; +$fa-var-cocktail: \f561; +$fa-var-face-surprise: \f5c2; +$fa-var-surprise: \f5c2; +$fa-var-bottle-water: \e4c5; +$fa-var-circle-pause: \f28b; +$fa-var-pause-circle: \f28b; +$fa-var-toilet-paper-slash: \e072; +$fa-var-apple-whole: \f5d1; +$fa-var-apple-alt: \f5d1; +$fa-var-kitchen-set: \e51a; +$fa-var-r: \52; +$fa-var-temperature-quarter: \f2ca; +$fa-var-temperature-1: \f2ca; +$fa-var-thermometer-1: \f2ca; +$fa-var-thermometer-quarter: \f2ca; +$fa-var-cube: \f1b2; +$fa-var-bitcoin-sign: \e0b4; +$fa-var-shield-dog: \e573; +$fa-var-solar-panel: \f5ba; +$fa-var-lock-open: \f3c1; +$fa-var-elevator: \e16d; +$fa-var-money-bill-transfer: \e528; +$fa-var-money-bill-trend-up: \e529; +$fa-var-house-flood-water-circle-arrow-right: \e50f; +$fa-var-square-poll-horizontal: \f682; +$fa-var-poll-h: \f682; +$fa-var-circle: \f111; +$fa-var-backward-fast: \f049; +$fa-var-fast-backward: \f049; +$fa-var-recycle: \f1b8; +$fa-var-user-astronaut: \f4fb; +$fa-var-plane-slash: \e069; +$fa-var-trademark: \f25c; +$fa-var-basketball: \f434; +$fa-var-basketball-ball: \f434; +$fa-var-satellite-dish: \f7c0; +$fa-var-circle-up: \f35b; +$fa-var-arrow-alt-circle-up: \f35b; +$fa-var-mobile-screen-button: \f3cd; +$fa-var-mobile-alt: \f3cd; +$fa-var-volume-high: \f028; +$fa-var-volume-up: \f028; +$fa-var-users-rays: \e593; +$fa-var-wallet: \f555; +$fa-var-clipboard-check: \f46c; +$fa-var-file-audio: \f1c7; +$fa-var-burger: \f805; +$fa-var-hamburger: \f805; +$fa-var-wrench: \f0ad; +$fa-var-bugs: \e4d0; +$fa-var-rupee-sign: \f156; +$fa-var-rupee: \f156; +$fa-var-file-image: \f1c5; +$fa-var-circle-question: \f059; +$fa-var-question-circle: \f059; +$fa-var-plane-departure: \f5b0; +$fa-var-handshake-slash: \e060; +$fa-var-book-bookmark: \e0bb; +$fa-var-code-branch: \f126; +$fa-var-hat-cowboy: \f8c0; +$fa-var-bridge: \e4c8; +$fa-var-phone-flip: \f879; +$fa-var-phone-alt: \f879; +$fa-var-truck-front: \e2b7; +$fa-var-cat: \f6be; +$fa-var-anchor-circle-exclamation: \e4ab; +$fa-var-truck-field: \e58d; +$fa-var-route: \f4d7; +$fa-var-clipboard-question: \e4e3; +$fa-var-panorama: \e209; +$fa-var-comment-medical: \f7f5; +$fa-var-teeth-open: \f62f; +$fa-var-file-circle-minus: \e4ed; +$fa-var-tags: \f02c; +$fa-var-wine-glass: \f4e3; +$fa-var-forward-fast: \f050; +$fa-var-fast-forward: \f050; +$fa-var-face-meh-blank: \f5a4; +$fa-var-meh-blank: \f5a4; +$fa-var-square-parking: \f540; +$fa-var-parking: \f540; +$fa-var-house-signal: \e012; +$fa-var-bars-progress: \f828; +$fa-var-tasks-alt: \f828; +$fa-var-faucet-drip: \e006; +$fa-var-cart-flatbed: \f474; +$fa-var-dolly-flatbed: \f474; +$fa-var-ban-smoking: \f54d; +$fa-var-smoking-ban: \f54d; +$fa-var-terminal: \f120; +$fa-var-mobile-button: \f10b; +$fa-var-house-medical-flag: \e514; +$fa-var-basket-shopping: \f291; +$fa-var-shopping-basket: \f291; +$fa-var-tape: \f4db; +$fa-var-bus-simple: \f55e; +$fa-var-bus-alt: \f55e; +$fa-var-eye: \f06e; +$fa-var-face-sad-cry: \f5b3; +$fa-var-sad-cry: \f5b3; +$fa-var-audio-description: \f29e; +$fa-var-person-military-to-person: \e54c; +$fa-var-file-shield: \e4f0; +$fa-var-user-slash: \f506; +$fa-var-pen: \f304; +$fa-var-tower-observation: \e586; +$fa-var-file-code: \f1c9; +$fa-var-signal: \f012; +$fa-var-signal-5: \f012; +$fa-var-signal-perfect: \f012; +$fa-var-bus: \f207; +$fa-var-heart-circle-xmark: \e501; +$fa-var-house-chimney: \e3af; +$fa-var-home-lg: \e3af; +$fa-var-window-maximize: \f2d0; +$fa-var-face-frown: \f119; +$fa-var-frown: \f119; +$fa-var-prescription: \f5b1; +$fa-var-shop: \f54f; +$fa-var-store-alt: \f54f; +$fa-var-floppy-disk: \f0c7; +$fa-var-save: \f0c7; +$fa-var-vihara: \f6a7; +$fa-var-scale-unbalanced: \f515; +$fa-var-balance-scale-left: \f515; +$fa-var-sort-up: \f0de; +$fa-var-sort-asc: \f0de; +$fa-var-comment-dots: \f4ad; +$fa-var-commenting: \f4ad; +$fa-var-plant-wilt: \e5aa; +$fa-var-diamond: \f219; +$fa-var-face-grin-squint: \f585; +$fa-var-grin-squint: \f585; +$fa-var-hand-holding-dollar: \f4c0; +$fa-var-hand-holding-usd: \f4c0; +$fa-var-bacterium: \e05a; +$fa-var-hand-pointer: \f25a; +$fa-var-drum-steelpan: \f56a; +$fa-var-hand-scissors: \f257; +$fa-var-hands-praying: \f684; +$fa-var-praying-hands: \f684; +$fa-var-arrow-rotate-right: \f01e; +$fa-var-arrow-right-rotate: \f01e; +$fa-var-arrow-rotate-forward: \f01e; +$fa-var-redo: \f01e; +$fa-var-biohazard: \f780; +$fa-var-location-crosshairs: \f601; +$fa-var-location: \f601; +$fa-var-mars-double: \f227; +$fa-var-child-dress: \e59c; +$fa-var-users-between-lines: \e591; +$fa-var-lungs-virus: \e067; +$fa-var-face-grin-tears: \f588; +$fa-var-grin-tears: \f588; +$fa-var-phone: \f095; +$fa-var-calendar-xmark: \f273; +$fa-var-calendar-times: \f273; +$fa-var-child-reaching: \e59d; +$fa-var-head-side-virus: \e064; +$fa-var-user-gear: \f4fe; +$fa-var-user-cog: \f4fe; +$fa-var-arrow-up-1-9: \f163; +$fa-var-sort-numeric-up: \f163; +$fa-var-door-closed: \f52a; +$fa-var-shield-virus: \e06c; +$fa-var-dice-six: \f526; +$fa-var-mosquito-net: \e52c; +$fa-var-bridge-water: \e4ce; +$fa-var-person-booth: \f756; +$fa-var-text-width: \f035; +$fa-var-hat-wizard: \f6e8; +$fa-var-pen-fancy: \f5ac; +$fa-var-person-digging: \f85e; +$fa-var-digging: \f85e; +$fa-var-trash: \f1f8; +$fa-var-gauge-simple: \f629; +$fa-var-gauge-simple-med: \f629; +$fa-var-tachometer-average: \f629; +$fa-var-book-medical: \f7e6; +$fa-var-poo: \f2fe; +$fa-var-quote-right: \f10e; +$fa-var-quote-right-alt: \f10e; +$fa-var-shirt: \f553; +$fa-var-t-shirt: \f553; +$fa-var-tshirt: \f553; +$fa-var-cubes: \f1b3; +$fa-var-divide: \f529; +$fa-var-tenge-sign: \f7d7; +$fa-var-tenge: \f7d7; +$fa-var-headphones: \f025; +$fa-var-hands-holding: \f4c2; +$fa-var-hands-clapping: \e1a8; +$fa-var-republican: \f75e; +$fa-var-arrow-left: \f060; +$fa-var-person-circle-xmark: \e543; +$fa-var-ruler: \f545; +$fa-var-align-left: \f036; +$fa-var-dice-d6: \f6d1; +$fa-var-restroom: \f7bd; +$fa-var-j: \4a; +$fa-var-users-viewfinder: \e595; +$fa-var-file-video: \f1c8; +$fa-var-up-right-from-square: \f35d; +$fa-var-external-link-alt: \f35d; +$fa-var-table-cells: \f00a; +$fa-var-th: \f00a; +$fa-var-file-pdf: \f1c1; +$fa-var-book-bible: \f647; +$fa-var-bible: \f647; +$fa-var-o: \4f; +$fa-var-suitcase-medical: \f0fa; +$fa-var-medkit: \f0fa; +$fa-var-user-secret: \f21b; +$fa-var-otter: \f700; +$fa-var-person-dress: \f182; +$fa-var-female: \f182; +$fa-var-comment-dollar: \f651; +$fa-var-business-time: \f64a; +$fa-var-briefcase-clock: \f64a; +$fa-var-table-cells-large: \f009; +$fa-var-th-large: \f009; +$fa-var-book-tanakh: \f827; +$fa-var-tanakh: \f827; +$fa-var-phone-volume: \f2a0; +$fa-var-volume-control-phone: \f2a0; +$fa-var-hat-cowboy-side: \f8c1; +$fa-var-clipboard-user: \f7f3; +$fa-var-child: \f1ae; +$fa-var-lira-sign: \f195; +$fa-var-satellite: \f7bf; +$fa-var-plane-lock: \e558; +$fa-var-tag: \f02b; +$fa-var-comment: \f075; +$fa-var-cake-candles: \f1fd; +$fa-var-birthday-cake: \f1fd; +$fa-var-cake: \f1fd; +$fa-var-envelope: \f0e0; +$fa-var-angles-up: \f102; +$fa-var-angle-double-up: \f102; +$fa-var-paperclip: \f0c6; +$fa-var-arrow-right-to-city: \e4b3; +$fa-var-ribbon: \f4d6; +$fa-var-lungs: \f604; +$fa-var-arrow-up-9-1: \f887; +$fa-var-sort-numeric-up-alt: \f887; +$fa-var-litecoin-sign: \e1d3; +$fa-var-border-none: \f850; +$fa-var-circle-nodes: \e4e2; +$fa-var-parachute-box: \f4cd; +$fa-var-indent: \f03c; +$fa-var-truck-field-un: \e58e; +$fa-var-hourglass: \f254; +$fa-var-hourglass-empty: \f254; +$fa-var-mountain: \f6fc; +$fa-var-user-doctor: \f0f0; +$fa-var-user-md: \f0f0; +$fa-var-circle-info: \f05a; +$fa-var-info-circle: \f05a; +$fa-var-cloud-meatball: \f73b; +$fa-var-camera: \f030; +$fa-var-camera-alt: \f030; +$fa-var-square-virus: \e578; +$fa-var-meteor: \f753; +$fa-var-car-on: \e4dd; +$fa-var-sleigh: \f7cc; +$fa-var-arrow-down-1-9: \f162; +$fa-var-sort-numeric-asc: \f162; +$fa-var-sort-numeric-down: \f162; +$fa-var-hand-holding-droplet: \f4c1; +$fa-var-hand-holding-water: \f4c1; +$fa-var-water: \f773; +$fa-var-calendar-check: \f274; +$fa-var-braille: \f2a1; +$fa-var-prescription-bottle-medical: \f486; +$fa-var-prescription-bottle-alt: \f486; +$fa-var-landmark: \f66f; +$fa-var-truck: \f0d1; +$fa-var-crosshairs: \f05b; +$fa-var-person-cane: \e53c; +$fa-var-tent: \e57d; +$fa-var-vest-patches: \e086; +$fa-var-check-double: \f560; +$fa-var-arrow-down-a-z: \f15d; +$fa-var-sort-alpha-asc: \f15d; +$fa-var-sort-alpha-down: \f15d; +$fa-var-money-bill-wheat: \e52a; +$fa-var-cookie: \f563; +$fa-var-arrow-rotate-left: \f0e2; +$fa-var-arrow-left-rotate: \f0e2; +$fa-var-arrow-rotate-back: \f0e2; +$fa-var-arrow-rotate-backward: \f0e2; +$fa-var-undo: \f0e2; +$fa-var-hard-drive: \f0a0; +$fa-var-hdd: \f0a0; +$fa-var-face-grin-squint-tears: \f586; +$fa-var-grin-squint-tears: \f586; +$fa-var-dumbbell: \f44b; +$fa-var-rectangle-list: \f022; +$fa-var-list-alt: \f022; +$fa-var-tarp-droplet: \e57c; +$fa-var-house-medical-circle-check: \e511; +$fa-var-person-skiing-nordic: \f7ca; +$fa-var-skiing-nordic: \f7ca; +$fa-var-calendar-plus: \f271; +$fa-var-plane-arrival: \f5af; +$fa-var-circle-left: \f359; +$fa-var-arrow-alt-circle-left: \f359; +$fa-var-train-subway: \f239; +$fa-var-subway: \f239; +$fa-var-chart-gantt: \e0e4; +$fa-var-indian-rupee-sign: \e1bc; +$fa-var-indian-rupee: \e1bc; +$fa-var-inr: \e1bc; +$fa-var-crop-simple: \f565; +$fa-var-crop-alt: \f565; +$fa-var-money-bill-1: \f3d1; +$fa-var-money-bill-alt: \f3d1; +$fa-var-left-long: \f30a; +$fa-var-long-arrow-alt-left: \f30a; +$fa-var-dna: \f471; +$fa-var-virus-slash: \e075; +$fa-var-minus: \f068; +$fa-var-subtract: \f068; +$fa-var-chess: \f439; +$fa-var-arrow-left-long: \f177; +$fa-var-long-arrow-left: \f177; +$fa-var-plug-circle-check: \e55c; +$fa-var-street-view: \f21d; +$fa-var-franc-sign: \e18f; +$fa-var-volume-off: \f026; +$fa-var-hands-asl-interpreting: \f2a3; +$fa-var-american-sign-language-interpreting: \f2a3; +$fa-var-asl-interpreting: \f2a3; +$fa-var-hands-american-sign-language-interpreting: \f2a3; +$fa-var-gear: \f013; +$fa-var-cog: \f013; +$fa-var-droplet-slash: \f5c7; +$fa-var-tint-slash: \f5c7; +$fa-var-mosque: \f678; +$fa-var-mosquito: \e52b; +$fa-var-star-of-david: \f69a; +$fa-var-person-military-rifle: \e54b; +$fa-var-cart-shopping: \f07a; +$fa-var-shopping-cart: \f07a; +$fa-var-vials: \f493; +$fa-var-plug-circle-plus: \e55f; +$fa-var-place-of-worship: \f67f; +$fa-var-grip-vertical: \f58e; +$fa-var-arrow-turn-up: \f148; +$fa-var-level-up: \f148; +$fa-var-u: \55; +$fa-var-square-root-variable: \f698; +$fa-var-square-root-alt: \f698; +$fa-var-clock: \f017; +$fa-var-clock-four: \f017; +$fa-var-backward-step: \f048; +$fa-var-step-backward: \f048; +$fa-var-pallet: \f482; +$fa-var-faucet: \e005; +$fa-var-baseball-bat-ball: \f432; +$fa-var-s: \53; +$fa-var-timeline: \e29c; +$fa-var-keyboard: \f11c; +$fa-var-caret-down: \f0d7; +$fa-var-house-chimney-medical: \f7f2; +$fa-var-clinic-medical: \f7f2; +$fa-var-temperature-three-quarters: \f2c8; +$fa-var-temperature-3: \f2c8; +$fa-var-thermometer-3: \f2c8; +$fa-var-thermometer-three-quarters: \f2c8; +$fa-var-mobile-screen: \f3cf; +$fa-var-mobile-android-alt: \f3cf; +$fa-var-plane-up: \e22d; +$fa-var-piggy-bank: \f4d3; +$fa-var-battery-half: \f242; +$fa-var-battery-3: \f242; +$fa-var-mountain-city: \e52e; +$fa-var-coins: \f51e; +$fa-var-khanda: \f66d; +$fa-var-sliders: \f1de; +$fa-var-sliders-h: \f1de; +$fa-var-folder-tree: \f802; +$fa-var-network-wired: \f6ff; +$fa-var-map-pin: \f276; +$fa-var-hamsa: \f665; +$fa-var-cent-sign: \e3f5; +$fa-var-flask: \f0c3; +$fa-var-person-pregnant: \e31e; +$fa-var-wand-sparkles: \f72b; +$fa-var-ellipsis-vertical: \f142; +$fa-var-ellipsis-v: \f142; +$fa-var-ticket: \f145; +$fa-var-power-off: \f011; +$fa-var-right-long: \f30b; +$fa-var-long-arrow-alt-right: \f30b; +$fa-var-flag-usa: \f74d; +$fa-var-laptop-file: \e51d; +$fa-var-tty: \f1e4; +$fa-var-teletype: \f1e4; +$fa-var-diagram-next: \e476; +$fa-var-person-rifle: \e54e; +$fa-var-house-medical-circle-exclamation: \e512; +$fa-var-closed-captioning: \f20a; +$fa-var-person-hiking: \f6ec; +$fa-var-hiking: \f6ec; +$fa-var-venus-double: \f226; +$fa-var-images: \f302; +$fa-var-calculator: \f1ec; +$fa-var-people-pulling: \e535; +$fa-var-n: \4e; +$fa-var-cable-car: \f7da; +$fa-var-tram: \f7da; +$fa-var-cloud-rain: \f73d; +$fa-var-building-circle-xmark: \e4d4; +$fa-var-ship: \f21a; +$fa-var-arrows-down-to-line: \e4b8; +$fa-var-download: \f019; +$fa-var-face-grin: \f580; +$fa-var-grin: \f580; +$fa-var-delete-left: \f55a; +$fa-var-backspace: \f55a; +$fa-var-eye-dropper: \f1fb; +$fa-var-eye-dropper-empty: \f1fb; +$fa-var-eyedropper: \f1fb; +$fa-var-file-circle-check: \e5a0; +$fa-var-forward: \f04e; +$fa-var-mobile: \f3ce; +$fa-var-mobile-android: \f3ce; +$fa-var-mobile-phone: \f3ce; +$fa-var-face-meh: \f11a; +$fa-var-meh: \f11a; +$fa-var-align-center: \f037; +$fa-var-book-skull: \f6b7; +$fa-var-book-dead: \f6b7; +$fa-var-id-card: \f2c2; +$fa-var-drivers-license: \f2c2; +$fa-var-outdent: \f03b; +$fa-var-dedent: \f03b; +$fa-var-heart-circle-exclamation: \e4fe; +$fa-var-house: \f015; +$fa-var-home: \f015; +$fa-var-home-alt: \f015; +$fa-var-home-lg-alt: \f015; +$fa-var-calendar-week: \f784; +$fa-var-laptop-medical: \f812; +$fa-var-b: \42; +$fa-var-file-medical: \f477; +$fa-var-dice-one: \f525; +$fa-var-kiwi-bird: \f535; +$fa-var-arrow-right-arrow-left: \f0ec; +$fa-var-exchange: \f0ec; +$fa-var-rotate-right: \f2f9; +$fa-var-redo-alt: \f2f9; +$fa-var-rotate-forward: \f2f9; +$fa-var-utensils: \f2e7; +$fa-var-cutlery: \f2e7; +$fa-var-arrow-up-wide-short: \f161; +$fa-var-sort-amount-up: \f161; +$fa-var-mill-sign: \e1ed; +$fa-var-bowl-rice: \e2eb; +$fa-var-skull: \f54c; +$fa-var-tower-broadcast: \f519; +$fa-var-broadcast-tower: \f519; +$fa-var-truck-pickup: \f63c; +$fa-var-up-long: \f30c; +$fa-var-long-arrow-alt-up: \f30c; +$fa-var-stop: \f04d; +$fa-var-code-merge: \f387; +$fa-var-upload: \f093; +$fa-var-hurricane: \f751; +$fa-var-mound: \e52d; +$fa-var-toilet-portable: \e583; +$fa-var-compact-disc: \f51f; +$fa-var-file-arrow-down: \f56d; +$fa-var-file-download: \f56d; +$fa-var-caravan: \f8ff; +$fa-var-shield-cat: \e572; +$fa-var-bolt: \f0e7; +$fa-var-zap: \f0e7; +$fa-var-glass-water: \e4f4; +$fa-var-oil-well: \e532; +$fa-var-vault: \e2c5; +$fa-var-mars: \f222; +$fa-var-toilet: \f7d8; +$fa-var-plane-circle-xmark: \e557; +$fa-var-yen-sign: \f157; +$fa-var-cny: \f157; +$fa-var-jpy: \f157; +$fa-var-rmb: \f157; +$fa-var-yen: \f157; +$fa-var-ruble-sign: \f158; +$fa-var-rouble: \f158; +$fa-var-rub: \f158; +$fa-var-ruble: \f158; +$fa-var-sun: \f185; +$fa-var-guitar: \f7a6; +$fa-var-face-laugh-wink: \f59c; +$fa-var-laugh-wink: \f59c; +$fa-var-horse-head: \f7ab; +$fa-var-bore-hole: \e4c3; +$fa-var-industry: \f275; +$fa-var-circle-down: \f358; +$fa-var-arrow-alt-circle-down: \f358; +$fa-var-arrows-turn-to-dots: \e4c1; +$fa-var-florin-sign: \e184; +$fa-var-arrow-down-short-wide: \f884; +$fa-var-sort-amount-desc: \f884; +$fa-var-sort-amount-down-alt: \f884; +$fa-var-less-than: \3c; +$fa-var-angle-down: \f107; +$fa-var-car-tunnel: \e4de; +$fa-var-head-side-cough: \e061; +$fa-var-grip-lines: \f7a4; +$fa-var-thumbs-down: \f165; +$fa-var-user-lock: \f502; +$fa-var-arrow-right-long: \f178; +$fa-var-long-arrow-right: \f178; +$fa-var-anchor-circle-xmark: \e4ac; +$fa-var-ellipsis: \f141; +$fa-var-ellipsis-h: \f141; +$fa-var-chess-pawn: \f443; +$fa-var-kit-medical: \f479; +$fa-var-first-aid: \f479; +$fa-var-person-through-window: \e5a9; +$fa-var-toolbox: \f552; +$fa-var-hands-holding-circle: \e4fb; +$fa-var-bug: \f188; +$fa-var-credit-card: \f09d; +$fa-var-credit-card-alt: \f09d; +$fa-var-car: \f1b9; +$fa-var-automobile: \f1b9; +$fa-var-hand-holding-hand: \e4f7; +$fa-var-book-open-reader: \f5da; +$fa-var-book-reader: \f5da; +$fa-var-mountain-sun: \e52f; +$fa-var-arrows-left-right-to-line: \e4ba; +$fa-var-dice-d20: \f6cf; +$fa-var-truck-droplet: \e58c; +$fa-var-file-circle-xmark: \e5a1; +$fa-var-temperature-arrow-up: \e040; +$fa-var-temperature-up: \e040; +$fa-var-medal: \f5a2; +$fa-var-bed: \f236; +$fa-var-square-h: \f0fd; +$fa-var-h-square: \f0fd; +$fa-var-podcast: \f2ce; +$fa-var-temperature-full: \f2c7; +$fa-var-temperature-4: \f2c7; +$fa-var-thermometer-4: \f2c7; +$fa-var-thermometer-full: \f2c7; +$fa-var-bell: \f0f3; +$fa-var-superscript: \f12b; +$fa-var-plug-circle-xmark: \e560; +$fa-var-star-of-life: \f621; +$fa-var-phone-slash: \f3dd; +$fa-var-paint-roller: \f5aa; +$fa-var-handshake-angle: \f4c4; +$fa-var-hands-helping: \f4c4; +$fa-var-location-dot: \f3c5; +$fa-var-map-marker-alt: \f3c5; +$fa-var-file: \f15b; +$fa-var-greater-than: \3e; +$fa-var-person-swimming: \f5c4; +$fa-var-swimmer: \f5c4; +$fa-var-arrow-down: \f063; +$fa-var-droplet: \f043; +$fa-var-tint: \f043; +$fa-var-eraser: \f12d; +$fa-var-earth-americas: \f57d; +$fa-var-earth: \f57d; +$fa-var-earth-america: \f57d; +$fa-var-globe-americas: \f57d; +$fa-var-person-burst: \e53b; +$fa-var-dove: \f4ba; +$fa-var-battery-empty: \f244; +$fa-var-battery-0: \f244; +$fa-var-socks: \f696; +$fa-var-inbox: \f01c; +$fa-var-section: \e447; +$fa-var-gauge-high: \f625; +$fa-var-tachometer-alt: \f625; +$fa-var-tachometer-alt-fast: \f625; +$fa-var-envelope-open-text: \f658; +$fa-var-hospital: \f0f8; +$fa-var-hospital-alt: \f0f8; +$fa-var-hospital-wide: \f0f8; +$fa-var-wine-bottle: \f72f; +$fa-var-chess-rook: \f447; +$fa-var-bars-staggered: \f550; +$fa-var-reorder: \f550; +$fa-var-stream: \f550; +$fa-var-dharmachakra: \f655; +$fa-var-hotdog: \f80f; +$fa-var-person-walking-with-cane: \f29d; +$fa-var-blind: \f29d; +$fa-var-drum: \f569; +$fa-var-ice-cream: \f810; +$fa-var-heart-circle-bolt: \e4fc; +$fa-var-fax: \f1ac; +$fa-var-paragraph: \f1dd; +$fa-var-check-to-slot: \f772; +$fa-var-vote-yea: \f772; +$fa-var-star-half: \f089; +$fa-var-boxes-stacked: \f468; +$fa-var-boxes: \f468; +$fa-var-boxes-alt: \f468; +$fa-var-link: \f0c1; +$fa-var-chain: \f0c1; +$fa-var-ear-listen: \f2a2; +$fa-var-assistive-listening-systems: \f2a2; +$fa-var-tree-city: \e587; +$fa-var-play: \f04b; +$fa-var-font: \f031; +$fa-var-rupiah-sign: \e23d; +$fa-var-magnifying-glass: \f002; +$fa-var-search: \f002; +$fa-var-table-tennis-paddle-ball: \f45d; +$fa-var-ping-pong-paddle-ball: \f45d; +$fa-var-table-tennis: \f45d; +$fa-var-person-dots-from-line: \f470; +$fa-var-diagnoses: \f470; +$fa-var-trash-can-arrow-up: \f82a; +$fa-var-trash-restore-alt: \f82a; +$fa-var-naira-sign: \e1f6; +$fa-var-cart-arrow-down: \f218; +$fa-var-walkie-talkie: \f8ef; +$fa-var-file-pen: \f31c; +$fa-var-file-edit: \f31c; +$fa-var-receipt: \f543; +$fa-var-square-pen: \f14b; +$fa-var-pen-square: \f14b; +$fa-var-pencil-square: \f14b; +$fa-var-suitcase-rolling: \f5c1; +$fa-var-person-circle-exclamation: \e53f; +$fa-var-chevron-down: \f078; +$fa-var-battery-full: \f240; +$fa-var-battery: \f240; +$fa-var-battery-5: \f240; +$fa-var-skull-crossbones: \f714; +$fa-var-code-compare: \e13a; +$fa-var-list-ul: \f0ca; +$fa-var-list-dots: \f0ca; +$fa-var-school-lock: \e56f; +$fa-var-tower-cell: \e585; +$fa-var-down-long: \f309; +$fa-var-long-arrow-alt-down: \f309; +$fa-var-ranking-star: \e561; +$fa-var-chess-king: \f43f; +$fa-var-person-harassing: \e549; +$fa-var-brazilian-real-sign: \e46c; +$fa-var-landmark-dome: \f752; +$fa-var-landmark-alt: \f752; +$fa-var-arrow-up: \f062; +$fa-var-tv: \f26c; +$fa-var-television: \f26c; +$fa-var-tv-alt: \f26c; +$fa-var-shrimp: \e448; +$fa-var-list-check: \f0ae; +$fa-var-tasks: \f0ae; +$fa-var-jug-detergent: \e519; +$fa-var-circle-user: \f2bd; +$fa-var-user-circle: \f2bd; +$fa-var-user-shield: \f505; +$fa-var-wind: \f72e; +$fa-var-car-burst: \f5e1; +$fa-var-car-crash: \f5e1; +$fa-var-y: \59; +$fa-var-person-snowboarding: \f7ce; +$fa-var-snowboarding: \f7ce; +$fa-var-truck-fast: \f48b; +$fa-var-shipping-fast: \f48b; +$fa-var-fish: \f578; +$fa-var-user-graduate: \f501; +$fa-var-circle-half-stroke: \f042; +$fa-var-adjust: \f042; +$fa-var-clapperboard: \e131; +$fa-var-circle-radiation: \f7ba; +$fa-var-radiation-alt: \f7ba; +$fa-var-baseball: \f433; +$fa-var-baseball-ball: \f433; +$fa-var-jet-fighter-up: \e518; +$fa-var-diagram-project: \f542; +$fa-var-project-diagram: \f542; +$fa-var-copy: \f0c5; +$fa-var-volume-xmark: \f6a9; +$fa-var-volume-mute: \f6a9; +$fa-var-volume-times: \f6a9; +$fa-var-hand-sparkles: \e05d; +$fa-var-grip: \f58d; +$fa-var-grip-horizontal: \f58d; +$fa-var-share-from-square: \f14d; +$fa-var-share-square: \f14d; +$fa-var-child-combatant: \e4e0; +$fa-var-child-rifle: \e4e0; +$fa-var-gun: \e19b; +$fa-var-square-phone: \f098; +$fa-var-phone-square: \f098; +$fa-var-plus: \2b; +$fa-var-add: \2b; +$fa-var-expand: \f065; +$fa-var-computer: \e4e5; +$fa-var-xmark: \f00d; +$fa-var-close: \f00d; +$fa-var-multiply: \f00d; +$fa-var-remove: \f00d; +$fa-var-times: \f00d; +$fa-var-arrows-up-down-left-right: \f047; +$fa-var-arrows: \f047; +$fa-var-chalkboard-user: \f51c; +$fa-var-chalkboard-teacher: \f51c; +$fa-var-peso-sign: \e222; +$fa-var-building-shield: \e4d8; +$fa-var-baby: \f77c; +$fa-var-users-line: \e592; +$fa-var-quote-left: \f10d; +$fa-var-quote-left-alt: \f10d; +$fa-var-tractor: \f722; +$fa-var-trash-arrow-up: \f829; +$fa-var-trash-restore: \f829; +$fa-var-arrow-down-up-lock: \e4b0; +$fa-var-lines-leaning: \e51e; +$fa-var-ruler-combined: \f546; +$fa-var-copyright: \f1f9; +$fa-var-equals: \3d; +$fa-var-blender: \f517; +$fa-var-teeth: \f62e; +$fa-var-shekel-sign: \f20b; +$fa-var-ils: \f20b; +$fa-var-shekel: \f20b; +$fa-var-sheqel: \f20b; +$fa-var-sheqel-sign: \f20b; +$fa-var-map: \f279; +$fa-var-rocket: \f135; +$fa-var-photo-film: \f87c; +$fa-var-photo-video: \f87c; +$fa-var-folder-minus: \f65d; +$fa-var-store: \f54e; +$fa-var-arrow-trend-up: \e098; +$fa-var-plug-circle-minus: \e55e; +$fa-var-sign-hanging: \f4d9; +$fa-var-sign: \f4d9; +$fa-var-bezier-curve: \f55b; +$fa-var-bell-slash: \f1f6; +$fa-var-tablet: \f3fb; +$fa-var-tablet-android: \f3fb; +$fa-var-school-flag: \e56e; +$fa-var-fill: \f575; +$fa-var-angle-up: \f106; +$fa-var-drumstick-bite: \f6d7; +$fa-var-holly-berry: \f7aa; +$fa-var-chevron-left: \f053; +$fa-var-bacteria: \e059; +$fa-var-hand-lizard: \f258; +$fa-var-notdef: \e1fe; +$fa-var-disease: \f7fa; +$fa-var-briefcase-medical: \f469; +$fa-var-genderless: \f22d; +$fa-var-chevron-right: \f054; +$fa-var-retweet: \f079; +$fa-var-car-rear: \f5de; +$fa-var-car-alt: \f5de; +$fa-var-pump-soap: \e06b; +$fa-var-video-slash: \f4e2; +$fa-var-battery-quarter: \f243; +$fa-var-battery-2: \f243; +$fa-var-radio: \f8d7; +$fa-var-baby-carriage: \f77d; +$fa-var-carriage-baby: \f77d; +$fa-var-traffic-light: \f637; +$fa-var-thermometer: \f491; +$fa-var-vr-cardboard: \f729; +$fa-var-hand-middle-finger: \f806; +$fa-var-percent: \25; +$fa-var-percentage: \25; +$fa-var-truck-moving: \f4df; +$fa-var-glass-water-droplet: \e4f5; +$fa-var-display: \e163; +$fa-var-face-smile: \f118; +$fa-var-smile: \f118; +$fa-var-thumbtack: \f08d; +$fa-var-thumb-tack: \f08d; +$fa-var-trophy: \f091; +$fa-var-person-praying: \f683; +$fa-var-pray: \f683; +$fa-var-hammer: \f6e3; +$fa-var-hand-peace: \f25b; +$fa-var-rotate: \f2f1; +$fa-var-sync-alt: \f2f1; +$fa-var-spinner: \f110; +$fa-var-robot: \f544; +$fa-var-peace: \f67c; +$fa-var-gears: \f085; +$fa-var-cogs: \f085; +$fa-var-warehouse: \f494; +$fa-var-arrow-up-right-dots: \e4b7; +$fa-var-splotch: \f5bc; +$fa-var-face-grin-hearts: \f584; +$fa-var-grin-hearts: \f584; +$fa-var-dice-four: \f524; +$fa-var-sim-card: \f7c4; +$fa-var-transgender: \f225; +$fa-var-transgender-alt: \f225; +$fa-var-mercury: \f223; +$fa-var-arrow-turn-down: \f149; +$fa-var-level-down: \f149; +$fa-var-person-falling-burst: \e547; +$fa-var-award: \f559; +$fa-var-ticket-simple: \f3ff; +$fa-var-ticket-alt: \f3ff; +$fa-var-building: \f1ad; +$fa-var-angles-left: \f100; +$fa-var-angle-double-left: \f100; +$fa-var-qrcode: \f029; +$fa-var-clock-rotate-left: \f1da; +$fa-var-history: \f1da; +$fa-var-face-grin-beam-sweat: \f583; +$fa-var-grin-beam-sweat: \f583; +$fa-var-file-export: \f56e; +$fa-var-arrow-right-from-file: \f56e; +$fa-var-shield: \f132; +$fa-var-shield-blank: \f132; +$fa-var-arrow-up-short-wide: \f885; +$fa-var-sort-amount-up-alt: \f885; +$fa-var-house-medical: \e3b2; +$fa-var-golf-ball-tee: \f450; +$fa-var-golf-ball: \f450; +$fa-var-circle-chevron-left: \f137; +$fa-var-chevron-circle-left: \f137; +$fa-var-house-chimney-window: \e00d; +$fa-var-pen-nib: \f5ad; +$fa-var-tent-arrow-turn-left: \e580; +$fa-var-tents: \e582; +$fa-var-wand-magic: \f0d0; +$fa-var-magic: \f0d0; +$fa-var-dog: \f6d3; +$fa-var-carrot: \f787; +$fa-var-moon: \f186; +$fa-var-wine-glass-empty: \f5ce; +$fa-var-wine-glass-alt: \f5ce; +$fa-var-cheese: \f7ef; +$fa-var-yin-yang: \f6ad; +$fa-var-music: \f001; +$fa-var-code-commit: \f386; +$fa-var-temperature-low: \f76b; +$fa-var-person-biking: \f84a; +$fa-var-biking: \f84a; +$fa-var-broom: \f51a; +$fa-var-shield-heart: \e574; +$fa-var-gopuram: \f664; +$fa-var-earth-oceania: \e47b; +$fa-var-globe-oceania: \e47b; +$fa-var-square-xmark: \f2d3; +$fa-var-times-square: \f2d3; +$fa-var-xmark-square: \f2d3; +$fa-var-hashtag: \23; +$fa-var-up-right-and-down-left-from-center: \f424; +$fa-var-expand-alt: \f424; +$fa-var-oil-can: \f613; +$fa-var-t: \54; +$fa-var-hippo: \f6ed; +$fa-var-chart-column: \e0e3; +$fa-var-infinity: \f534; +$fa-var-vial-circle-check: \e596; +$fa-var-person-arrow-down-to-line: \e538; +$fa-var-voicemail: \f897; +$fa-var-fan: \f863; +$fa-var-person-walking-luggage: \e554; +$fa-var-up-down: \f338; +$fa-var-arrows-alt-v: \f338; +$fa-var-cloud-moon-rain: \f73c; +$fa-var-calendar: \f133; +$fa-var-trailer: \e041; +$fa-var-bahai: \f666; +$fa-var-haykal: \f666; +$fa-var-sd-card: \f7c2; +$fa-var-dragon: \f6d5; +$fa-var-shoe-prints: \f54b; +$fa-var-circle-plus: \f055; +$fa-var-plus-circle: \f055; +$fa-var-face-grin-tongue-wink: \f58b; +$fa-var-grin-tongue-wink: \f58b; +$fa-var-hand-holding: \f4bd; +$fa-var-plug-circle-exclamation: \e55d; +$fa-var-link-slash: \f127; +$fa-var-chain-broken: \f127; +$fa-var-chain-slash: \f127; +$fa-var-unlink: \f127; +$fa-var-clone: \f24d; +$fa-var-person-walking-arrow-loop-left: \e551; +$fa-var-arrow-up-z-a: \f882; +$fa-var-sort-alpha-up-alt: \f882; +$fa-var-fire-flame-curved: \f7e4; +$fa-var-fire-alt: \f7e4; +$fa-var-tornado: \f76f; +$fa-var-file-circle-plus: \e494; +$fa-var-book-quran: \f687; +$fa-var-quran: \f687; +$fa-var-anchor: \f13d; +$fa-var-border-all: \f84c; +$fa-var-face-angry: \f556; +$fa-var-angry: \f556; +$fa-var-cookie-bite: \f564; +$fa-var-arrow-trend-down: \e097; +$fa-var-rss: \f09e; +$fa-var-feed: \f09e; +$fa-var-draw-polygon: \f5ee; +$fa-var-scale-balanced: \f24e; +$fa-var-balance-scale: \f24e; +$fa-var-gauge-simple-high: \f62a; +$fa-var-tachometer: \f62a; +$fa-var-tachometer-fast: \f62a; +$fa-var-shower: \f2cc; +$fa-var-desktop: \f390; +$fa-var-desktop-alt: \f390; +$fa-var-m: \4d; +$fa-var-table-list: \f00b; +$fa-var-th-list: \f00b; +$fa-var-comment-sms: \f7cd; +$fa-var-sms: \f7cd; +$fa-var-book: \f02d; +$fa-var-user-plus: \f234; +$fa-var-check: \f00c; +$fa-var-battery-three-quarters: \f241; +$fa-var-battery-4: \f241; +$fa-var-house-circle-check: \e509; +$fa-var-angle-left: \f104; +$fa-var-diagram-successor: \e47a; +$fa-var-truck-arrow-right: \e58b; +$fa-var-arrows-split-up-and-left: \e4bc; +$fa-var-hand-fist: \f6de; +$fa-var-fist-raised: \f6de; +$fa-var-cloud-moon: \f6c3; +$fa-var-briefcase: \f0b1; +$fa-var-person-falling: \e546; +$fa-var-image-portrait: \f3e0; +$fa-var-portrait: \f3e0; +$fa-var-user-tag: \f507; +$fa-var-rug: \e569; +$fa-var-earth-europe: \f7a2; +$fa-var-globe-europe: \f7a2; +$fa-var-cart-flatbed-suitcase: \f59d; +$fa-var-luggage-cart: \f59d; +$fa-var-rectangle-xmark: \f410; +$fa-var-rectangle-times: \f410; +$fa-var-times-rectangle: \f410; +$fa-var-window-close: \f410; +$fa-var-baht-sign: \e0ac; +$fa-var-book-open: \f518; +$fa-var-book-journal-whills: \f66a; +$fa-var-journal-whills: \f66a; +$fa-var-handcuffs: \e4f8; +$fa-var-triangle-exclamation: \f071; +$fa-var-exclamation-triangle: \f071; +$fa-var-warning: \f071; +$fa-var-database: \f1c0; +$fa-var-share: \f064; +$fa-var-arrow-turn-right: \f064; +$fa-var-mail-forward: \f064; +$fa-var-bottle-droplet: \e4c4; +$fa-var-mask-face: \e1d7; +$fa-var-hill-rockslide: \e508; +$fa-var-right-left: \f362; +$fa-var-exchange-alt: \f362; +$fa-var-paper-plane: \f1d8; +$fa-var-road-circle-exclamation: \e565; +$fa-var-dungeon: \f6d9; +$fa-var-align-right: \f038; +$fa-var-money-bill-1-wave: \f53b; +$fa-var-money-bill-wave-alt: \f53b; +$fa-var-life-ring: \f1cd; +$fa-var-hands: \f2a7; +$fa-var-sign-language: \f2a7; +$fa-var-signing: \f2a7; +$fa-var-calendar-day: \f783; +$fa-var-water-ladder: \f5c5; +$fa-var-ladder-water: \f5c5; +$fa-var-swimming-pool: \f5c5; +$fa-var-arrows-up-down: \f07d; +$fa-var-arrows-v: \f07d; +$fa-var-face-grimace: \f57f; +$fa-var-grimace: \f57f; +$fa-var-wheelchair-move: \e2ce; +$fa-var-wheelchair-alt: \e2ce; +$fa-var-turn-down: \f3be; +$fa-var-level-down-alt: \f3be; +$fa-var-person-walking-arrow-right: \e552; +$fa-var-square-envelope: \f199; +$fa-var-envelope-square: \f199; +$fa-var-dice: \f522; +$fa-var-bowling-ball: \f436; +$fa-var-brain: \f5dc; +$fa-var-bandage: \f462; +$fa-var-band-aid: \f462; +$fa-var-calendar-minus: \f272; +$fa-var-circle-xmark: \f057; +$fa-var-times-circle: \f057; +$fa-var-xmark-circle: \f057; +$fa-var-gifts: \f79c; +$fa-var-hotel: \f594; +$fa-var-earth-asia: \f57e; +$fa-var-globe-asia: \f57e; +$fa-var-id-card-clip: \f47f; +$fa-var-id-card-alt: \f47f; +$fa-var-magnifying-glass-plus: \f00e; +$fa-var-search-plus: \f00e; +$fa-var-thumbs-up: \f164; +$fa-var-user-clock: \f4fd; +$fa-var-hand-dots: \f461; +$fa-var-allergies: \f461; +$fa-var-file-invoice: \f570; +$fa-var-window-minimize: \f2d1; +$fa-var-mug-saucer: \f0f4; +$fa-var-coffee: \f0f4; +$fa-var-brush: \f55d; +$fa-var-mask: \f6fa; +$fa-var-magnifying-glass-minus: \f010; +$fa-var-search-minus: \f010; +$fa-var-ruler-vertical: \f548; +$fa-var-user-large: \f406; +$fa-var-user-alt: \f406; +$fa-var-train-tram: \e5b4; +$fa-var-user-nurse: \f82f; +$fa-var-syringe: \f48e; +$fa-var-cloud-sun: \f6c4; +$fa-var-stopwatch-20: \e06f; +$fa-var-square-full: \f45c; +$fa-var-magnet: \f076; +$fa-var-jar: \e516; +$fa-var-note-sticky: \f249; +$fa-var-sticky-note: \f249; +$fa-var-bug-slash: \e490; +$fa-var-arrow-up-from-water-pump: \e4b6; +$fa-var-bone: \f5d7; +$fa-var-user-injured: \f728; +$fa-var-face-sad-tear: \f5b4; +$fa-var-sad-tear: \f5b4; +$fa-var-plane: \f072; +$fa-var-tent-arrows-down: \e581; +$fa-var-exclamation: \21; +$fa-var-arrows-spin: \e4bb; +$fa-var-print: \f02f; +$fa-var-turkish-lira-sign: \e2bb; +$fa-var-try: \e2bb; +$fa-var-turkish-lira: \e2bb; +$fa-var-dollar-sign: \24; +$fa-var-dollar: \24; +$fa-var-usd: \24; +$fa-var-x: \58; +$fa-var-magnifying-glass-dollar: \f688; +$fa-var-search-dollar: \f688; +$fa-var-users-gear: \f509; +$fa-var-users-cog: \f509; +$fa-var-person-military-pointing: \e54a; +$fa-var-building-columns: \f19c; +$fa-var-bank: \f19c; +$fa-var-institution: \f19c; +$fa-var-museum: \f19c; +$fa-var-university: \f19c; +$fa-var-umbrella: \f0e9; +$fa-var-trowel: \e589; +$fa-var-d: \44; +$fa-var-stapler: \e5af; +$fa-var-masks-theater: \f630; +$fa-var-theater-masks: \f630; +$fa-var-kip-sign: \e1c4; +$fa-var-hand-point-left: \f0a5; +$fa-var-handshake-simple: \f4c6; +$fa-var-handshake-alt: \f4c6; +$fa-var-jet-fighter: \f0fb; +$fa-var-fighter-jet: \f0fb; +$fa-var-square-share-nodes: \f1e1; +$fa-var-share-alt-square: \f1e1; +$fa-var-barcode: \f02a; +$fa-var-plus-minus: \e43c; +$fa-var-video: \f03d; +$fa-var-video-camera: \f03d; +$fa-var-graduation-cap: \f19d; +$fa-var-mortar-board: \f19d; +$fa-var-hand-holding-medical: \e05c; +$fa-var-person-circle-check: \e53e; +$fa-var-turn-up: \f3bf; +$fa-var-level-up-alt: \f3bf; + +$fa-var-monero: \f3d0; +$fa-var-hooli: \f427; +$fa-var-yelp: \f1e9; +$fa-var-cc-visa: \f1f0; +$fa-var-lastfm: \f202; +$fa-var-shopware: \f5b5; +$fa-var-creative-commons-nc: \f4e8; +$fa-var-aws: \f375; +$fa-var-redhat: \f7bc; +$fa-var-yoast: \f2b1; +$fa-var-cloudflare: \e07d; +$fa-var-ups: \f7e0; +$fa-var-wpexplorer: \f2de; +$fa-var-dyalog: \f399; +$fa-var-bity: \f37a; +$fa-var-stackpath: \f842; +$fa-var-buysellads: \f20d; +$fa-var-first-order: \f2b0; +$fa-var-modx: \f285; +$fa-var-guilded: \e07e; +$fa-var-vnv: \f40b; +$fa-var-square-js: \f3b9; +$fa-var-js-square: \f3b9; +$fa-var-microsoft: \f3ca; +$fa-var-qq: \f1d6; +$fa-var-orcid: \f8d2; +$fa-var-java: \f4e4; +$fa-var-invision: \f7b0; +$fa-var-creative-commons-pd-alt: \f4ed; +$fa-var-centercode: \f380; +$fa-var-glide-g: \f2a6; +$fa-var-drupal: \f1a9; +$fa-var-hire-a-helper: \f3b0; +$fa-var-creative-commons-by: \f4e7; +$fa-var-unity: \e049; +$fa-var-whmcs: \f40d; +$fa-var-rocketchat: \f3e8; +$fa-var-vk: \f189; +$fa-var-untappd: \f405; +$fa-var-mailchimp: \f59e; +$fa-var-css3-alt: \f38b; +$fa-var-square-reddit: \f1a2; +$fa-var-reddit-square: \f1a2; +$fa-var-vimeo-v: \f27d; +$fa-var-contao: \f26d; +$fa-var-square-font-awesome: \e5ad; +$fa-var-deskpro: \f38f; +$fa-var-sistrix: \f3ee; +$fa-var-square-instagram: \e055; +$fa-var-instagram-square: \e055; +$fa-var-battle-net: \f835; +$fa-var-the-red-yeti: \f69d; +$fa-var-square-hacker-news: \f3af; +$fa-var-hacker-news-square: \f3af; +$fa-var-edge: \f282; +$fa-var-napster: \f3d2; +$fa-var-square-snapchat: \f2ad; +$fa-var-snapchat-square: \f2ad; +$fa-var-google-plus-g: \f0d5; +$fa-var-artstation: \f77a; +$fa-var-markdown: \f60f; +$fa-var-sourcetree: \f7d3; +$fa-var-google-plus: \f2b3; +$fa-var-diaspora: \f791; +$fa-var-foursquare: \f180; +$fa-var-stack-overflow: \f16c; +$fa-var-github-alt: \f113; +$fa-var-phoenix-squadron: \f511; +$fa-var-pagelines: \f18c; +$fa-var-algolia: \f36c; +$fa-var-red-river: \f3e3; +$fa-var-creative-commons-sa: \f4ef; +$fa-var-safari: \f267; +$fa-var-google: \f1a0; +$fa-var-square-font-awesome-stroke: \f35c; +$fa-var-font-awesome-alt: \f35c; +$fa-var-atlassian: \f77b; +$fa-var-linkedin-in: \f0e1; +$fa-var-digital-ocean: \f391; +$fa-var-nimblr: \f5a8; +$fa-var-chromecast: \f838; +$fa-var-evernote: \f839; +$fa-var-hacker-news: \f1d4; +$fa-var-creative-commons-sampling: \f4f0; +$fa-var-adversal: \f36a; +$fa-var-creative-commons: \f25e; +$fa-var-watchman-monitoring: \e087; +$fa-var-fonticons: \f280; +$fa-var-weixin: \f1d7; +$fa-var-shirtsinbulk: \f214; +$fa-var-codepen: \f1cb; +$fa-var-git-alt: \f841; +$fa-var-lyft: \f3c3; +$fa-var-rev: \f5b2; +$fa-var-windows: \f17a; +$fa-var-wizards-of-the-coast: \f730; +$fa-var-square-viadeo: \f2aa; +$fa-var-viadeo-square: \f2aa; +$fa-var-meetup: \f2e0; +$fa-var-centos: \f789; +$fa-var-adn: \f170; +$fa-var-cloudsmith: \f384; +$fa-var-pied-piper-alt: \f1a8; +$fa-var-square-dribbble: \f397; +$fa-var-dribbble-square: \f397; +$fa-var-codiepie: \f284; +$fa-var-node: \f419; +$fa-var-mix: \f3cb; +$fa-var-steam: \f1b6; +$fa-var-cc-apple-pay: \f416; +$fa-var-scribd: \f28a; +$fa-var-openid: \f19b; +$fa-var-instalod: \e081; +$fa-var-expeditedssl: \f23e; +$fa-var-sellcast: \f2da; +$fa-var-square-twitter: \f081; +$fa-var-twitter-square: \f081; +$fa-var-r-project: \f4f7; +$fa-var-delicious: \f1a5; +$fa-var-freebsd: \f3a4; +$fa-var-vuejs: \f41f; +$fa-var-accusoft: \f369; +$fa-var-ioxhost: \f208; +$fa-var-fonticons-fi: \f3a2; +$fa-var-app-store: \f36f; +$fa-var-cc-mastercard: \f1f1; +$fa-var-itunes-note: \f3b5; +$fa-var-golang: \e40f; +$fa-var-kickstarter: \f3bb; +$fa-var-grav: \f2d6; +$fa-var-weibo: \f18a; +$fa-var-uncharted: \e084; +$fa-var-firstdraft: \f3a1; +$fa-var-square-youtube: \f431; +$fa-var-youtube-square: \f431; +$fa-var-wikipedia-w: \f266; +$fa-var-wpressr: \f3e4; +$fa-var-rendact: \f3e4; +$fa-var-angellist: \f209; +$fa-var-galactic-republic: \f50c; +$fa-var-nfc-directional: \e530; +$fa-var-skype: \f17e; +$fa-var-joget: \f3b7; +$fa-var-fedora: \f798; +$fa-var-stripe-s: \f42a; +$fa-var-meta: \e49b; +$fa-var-laravel: \f3bd; +$fa-var-hotjar: \f3b1; +$fa-var-bluetooth-b: \f294; +$fa-var-sticker-mule: \f3f7; +$fa-var-creative-commons-zero: \f4f3; +$fa-var-hips: \f452; +$fa-var-behance: \f1b4; +$fa-var-reddit: \f1a1; +$fa-var-discord: \f392; +$fa-var-chrome: \f268; +$fa-var-app-store-ios: \f370; +$fa-var-cc-discover: \f1f2; +$fa-var-wpbeginner: \f297; +$fa-var-confluence: \f78d; +$fa-var-mdb: \f8ca; +$fa-var-dochub: \f394; +$fa-var-accessible-icon: \f368; +$fa-var-ebay: \f4f4; +$fa-var-amazon: \f270; +$fa-var-unsplash: \e07c; +$fa-var-yarn: \f7e3; +$fa-var-square-steam: \f1b7; +$fa-var-steam-square: \f1b7; +$fa-var-500px: \f26e; +$fa-var-square-vimeo: \f194; +$fa-var-vimeo-square: \f194; +$fa-var-asymmetrik: \f372; +$fa-var-font-awesome: \f2b4; +$fa-var-font-awesome-flag: \f2b4; +$fa-var-font-awesome-logo-full: \f2b4; +$fa-var-gratipay: \f184; +$fa-var-apple: \f179; +$fa-var-hive: \e07f; +$fa-var-gitkraken: \f3a6; +$fa-var-keybase: \f4f5; +$fa-var-apple-pay: \f415; +$fa-var-padlet: \e4a0; +$fa-var-amazon-pay: \f42c; +$fa-var-square-github: \f092; +$fa-var-github-square: \f092; +$fa-var-stumbleupon: \f1a4; +$fa-var-fedex: \f797; +$fa-var-phoenix-framework: \f3dc; +$fa-var-shopify: \e057; +$fa-var-neos: \f612; +$fa-var-hackerrank: \f5f7; +$fa-var-researchgate: \f4f8; +$fa-var-swift: \f8e1; +$fa-var-angular: \f420; +$fa-var-speakap: \f3f3; +$fa-var-angrycreative: \f36e; +$fa-var-y-combinator: \f23b; +$fa-var-empire: \f1d1; +$fa-var-envira: \f299; +$fa-var-square-gitlab: \e5ae; +$fa-var-gitlab-square: \e5ae; +$fa-var-studiovinari: \f3f8; +$fa-var-pied-piper: \f2ae; +$fa-var-wordpress: \f19a; +$fa-var-product-hunt: \f288; +$fa-var-firefox: \f269; +$fa-var-linode: \f2b8; +$fa-var-goodreads: \f3a8; +$fa-var-square-odnoklassniki: \f264; +$fa-var-odnoklassniki-square: \f264; +$fa-var-jsfiddle: \f1cc; +$fa-var-sith: \f512; +$fa-var-themeisle: \f2b2; +$fa-var-page4: \f3d7; +$fa-var-hashnode: \e499; +$fa-var-react: \f41b; +$fa-var-cc-paypal: \f1f4; +$fa-var-squarespace: \f5be; +$fa-var-cc-stripe: \f1f5; +$fa-var-creative-commons-share: \f4f2; +$fa-var-bitcoin: \f379; +$fa-var-keycdn: \f3ba; +$fa-var-opera: \f26a; +$fa-var-itch-io: \f83a; +$fa-var-umbraco: \f8e8; +$fa-var-galactic-senate: \f50d; +$fa-var-ubuntu: \f7df; +$fa-var-draft2digital: \f396; +$fa-var-stripe: \f429; +$fa-var-houzz: \f27c; +$fa-var-gg: \f260; +$fa-var-dhl: \f790; +$fa-var-square-pinterest: \f0d3; +$fa-var-pinterest-square: \f0d3; +$fa-var-xing: \f168; +$fa-var-blackberry: \f37b; +$fa-var-creative-commons-pd: \f4ec; +$fa-var-playstation: \f3df; +$fa-var-quinscape: \f459; +$fa-var-less: \f41d; +$fa-var-blogger-b: \f37d; +$fa-var-opencart: \f23d; +$fa-var-vine: \f1ca; +$fa-var-paypal: \f1ed; +$fa-var-gitlab: \f296; +$fa-var-typo3: \f42b; +$fa-var-reddit-alien: \f281; +$fa-var-yahoo: \f19e; +$fa-var-dailymotion: \e052; +$fa-var-affiliatetheme: \f36b; +$fa-var-pied-piper-pp: \f1a7; +$fa-var-bootstrap: \f836; +$fa-var-odnoklassniki: \f263; +$fa-var-nfc-symbol: \e531; +$fa-var-ethereum: \f42e; +$fa-var-speaker-deck: \f83c; +$fa-var-creative-commons-nc-eu: \f4e9; +$fa-var-patreon: \f3d9; +$fa-var-avianex: \f374; +$fa-var-ello: \f5f1; +$fa-var-gofore: \f3a7; +$fa-var-bimobject: \f378; +$fa-var-facebook-f: \f39e; +$fa-var-square-google-plus: \f0d4; +$fa-var-google-plus-square: \f0d4; +$fa-var-mandalorian: \f50f; +$fa-var-first-order-alt: \f50a; +$fa-var-osi: \f41a; +$fa-var-google-wallet: \f1ee; +$fa-var-d-and-d-beyond: \f6ca; +$fa-var-periscope: \f3da; +$fa-var-fulcrum: \f50b; +$fa-var-cloudscale: \f383; +$fa-var-forumbee: \f211; +$fa-var-mizuni: \f3cc; +$fa-var-schlix: \f3ea; +$fa-var-square-xing: \f169; +$fa-var-xing-square: \f169; +$fa-var-bandcamp: \f2d5; +$fa-var-wpforms: \f298; +$fa-var-cloudversify: \f385; +$fa-var-usps: \f7e1; +$fa-var-megaport: \f5a3; +$fa-var-magento: \f3c4; +$fa-var-spotify: \f1bc; +$fa-var-optin-monster: \f23c; +$fa-var-fly: \f417; +$fa-var-aviato: \f421; +$fa-var-itunes: \f3b4; +$fa-var-cuttlefish: \f38c; +$fa-var-blogger: \f37c; +$fa-var-flickr: \f16e; +$fa-var-viber: \f409; +$fa-var-soundcloud: \f1be; +$fa-var-digg: \f1a6; +$fa-var-tencent-weibo: \f1d5; +$fa-var-symfony: \f83d; +$fa-var-maxcdn: \f136; +$fa-var-etsy: \f2d7; +$fa-var-facebook-messenger: \f39f; +$fa-var-audible: \f373; +$fa-var-think-peaks: \f731; +$fa-var-bilibili: \e3d9; +$fa-var-erlang: \f39d; +$fa-var-cotton-bureau: \f89e; +$fa-var-dashcube: \f210; +$fa-var-42-group: \e080; +$fa-var-innosoft: \e080; +$fa-var-stack-exchange: \f18d; +$fa-var-elementor: \f430; +$fa-var-square-pied-piper: \e01e; +$fa-var-pied-piper-square: \e01e; +$fa-var-creative-commons-nd: \f4eb; +$fa-var-palfed: \f3d8; +$fa-var-superpowers: \f2dd; +$fa-var-resolving: \f3e7; +$fa-var-xbox: \f412; +$fa-var-searchengin: \f3eb; +$fa-var-tiktok: \e07b; +$fa-var-square-facebook: \f082; +$fa-var-facebook-square: \f082; +$fa-var-renren: \f18b; +$fa-var-linux: \f17c; +$fa-var-glide: \f2a5; +$fa-var-linkedin: \f08c; +$fa-var-hubspot: \f3b2; +$fa-var-deploydog: \f38e; +$fa-var-twitch: \f1e8; +$fa-var-ravelry: \f2d9; +$fa-var-mixer: \e056; +$fa-var-square-lastfm: \f203; +$fa-var-lastfm-square: \f203; +$fa-var-vimeo: \f40a; +$fa-var-mendeley: \f7b3; +$fa-var-uniregistry: \f404; +$fa-var-figma: \f799; +$fa-var-creative-commons-remix: \f4ee; +$fa-var-cc-amazon-pay: \f42d; +$fa-var-dropbox: \f16b; +$fa-var-instagram: \f16d; +$fa-var-cmplid: \e360; +$fa-var-facebook: \f09a; +$fa-var-gripfire: \f3ac; +$fa-var-jedi-order: \f50e; +$fa-var-uikit: \f403; +$fa-var-fort-awesome-alt: \f3a3; +$fa-var-phabricator: \f3db; +$fa-var-ussunnah: \f407; +$fa-var-earlybirds: \f39a; +$fa-var-trade-federation: \f513; +$fa-var-autoprefixer: \f41c; +$fa-var-whatsapp: \f232; +$fa-var-slideshare: \f1e7; +$fa-var-google-play: \f3ab; +$fa-var-viadeo: \f2a9; +$fa-var-line: \f3c0; +$fa-var-google-drive: \f3aa; +$fa-var-servicestack: \f3ec; +$fa-var-simplybuilt: \f215; +$fa-var-bitbucket: \f171; +$fa-var-imdb: \f2d8; +$fa-var-deezer: \e077; +$fa-var-raspberry-pi: \f7bb; +$fa-var-jira: \f7b1; +$fa-var-docker: \f395; +$fa-var-screenpal: \e570; +$fa-var-bluetooth: \f293; +$fa-var-gitter: \f426; +$fa-var-d-and-d: \f38d; +$fa-var-microblog: \e01a; +$fa-var-cc-diners-club: \f24c; +$fa-var-gg-circle: \f261; +$fa-var-pied-piper-hat: \f4e5; +$fa-var-kickstarter-k: \f3bc; +$fa-var-yandex: \f413; +$fa-var-readme: \f4d5; +$fa-var-html5: \f13b; +$fa-var-sellsy: \f213; +$fa-var-sass: \f41e; +$fa-var-wirsindhandwerk: \e2d0; +$fa-var-wsh: \e2d0; +$fa-var-buromobelexperte: \f37f; +$fa-var-salesforce: \f83b; +$fa-var-octopus-deploy: \e082; +$fa-var-medapps: \f3c6; +$fa-var-ns8: \f3d5; +$fa-var-pinterest-p: \f231; +$fa-var-apper: \f371; +$fa-var-fort-awesome: \f286; +$fa-var-waze: \f83f; +$fa-var-cc-jcb: \f24b; +$fa-var-snapchat: \f2ab; +$fa-var-snapchat-ghost: \f2ab; +$fa-var-fantasy-flight-games: \f6dc; +$fa-var-rust: \e07a; +$fa-var-wix: \f5cf; +$fa-var-square-behance: \f1b5; +$fa-var-behance-square: \f1b5; +$fa-var-supple: \f3f9; +$fa-var-rebel: \f1d0; +$fa-var-css3: \f13c; +$fa-var-staylinked: \f3f5; +$fa-var-kaggle: \f5fa; +$fa-var-space-awesome: \e5ac; +$fa-var-deviantart: \f1bd; +$fa-var-cpanel: \f388; +$fa-var-goodreads-g: \f3a9; +$fa-var-square-git: \f1d2; +$fa-var-git-square: \f1d2; +$fa-var-square-tumblr: \f174; +$fa-var-tumblr-square: \f174; +$fa-var-trello: \f181; +$fa-var-creative-commons-nc-jp: \f4ea; +$fa-var-get-pocket: \f265; +$fa-var-perbyte: \e083; +$fa-var-grunt: \f3ad; +$fa-var-weebly: \f5cc; +$fa-var-connectdevelop: \f20e; +$fa-var-leanpub: \f212; +$fa-var-black-tie: \f27e; +$fa-var-themeco: \f5c6; +$fa-var-python: \f3e2; +$fa-var-android: \f17b; +$fa-var-bots: \e340; +$fa-var-free-code-camp: \f2c5; +$fa-var-hornbill: \f592; +$fa-var-js: \f3b8; +$fa-var-ideal: \e013; +$fa-var-git: \f1d3; +$fa-var-dev: \f6cc; +$fa-var-sketch: \f7c6; +$fa-var-yandex-international: \f414; +$fa-var-cc-amex: \f1f3; +$fa-var-uber: \f402; +$fa-var-github: \f09b; +$fa-var-php: \f457; +$fa-var-alipay: \f642; +$fa-var-youtube: \f167; +$fa-var-skyatlas: \f216; +$fa-var-firefox-browser: \e007; +$fa-var-replyd: \f3e6; +$fa-var-suse: \f7d6; +$fa-var-jenkins: \f3b6; +$fa-var-twitter: \f099; +$fa-var-rockrms: \f3e9; +$fa-var-pinterest: \f0d2; +$fa-var-buffer: \f837; +$fa-var-npm: \f3d4; +$fa-var-yammer: \f840; +$fa-var-btc: \f15a; +$fa-var-dribbble: \f17d; +$fa-var-stumbleupon-circle: \f1a3; +$fa-var-internet-explorer: \f26b; +$fa-var-stubber: \e5c7; +$fa-var-telegram: \f2c6; +$fa-var-telegram-plane: \f2c6; +$fa-var-old-republic: \f510; +$fa-var-odysee: \e5c6; +$fa-var-square-whatsapp: \f40c; +$fa-var-whatsapp-square: \f40c; +$fa-var-node-js: \f3d3; +$fa-var-edge-legacy: \e078; +$fa-var-slack: \f198; +$fa-var-slack-hash: \f198; +$fa-var-medrt: \f3c8; +$fa-var-usb: \f287; +$fa-var-tumblr: \f173; +$fa-var-vaadin: \f408; +$fa-var-quora: \f2c4; +$fa-var-reacteurope: \f75d; +$fa-var-medium: \f23a; +$fa-var-medium-m: \f23a; +$fa-var-amilia: \f36d; +$fa-var-mixcloud: \f289; +$fa-var-flipboard: \f44d; +$fa-var-viacoin: \f237; +$fa-var-critical-role: \f6c9; +$fa-var-sitrox: \e44a; +$fa-var-discourse: \f393; +$fa-var-joomla: \f1aa; +$fa-var-mastodon: \f4f6; +$fa-var-airbnb: \f834; +$fa-var-wolf-pack-battalion: \f514; +$fa-var-buy-n-large: \f8a6; +$fa-var-gulp: \f3ae; +$fa-var-creative-commons-sampling-plus: \f4f1; +$fa-var-strava: \f428; +$fa-var-ember: \f423; +$fa-var-canadian-maple-leaf: \f785; +$fa-var-teamspeak: \f4f9; +$fa-var-pushed: \f3e1; +$fa-var-wordpress-simple: \f411; +$fa-var-nutritionix: \f3d6; +$fa-var-wodu: \e088; +$fa-var-google-pay: \e079; +$fa-var-intercom: \f7af; +$fa-var-zhihu: \f63f; +$fa-var-korvue: \f42f; +$fa-var-pix: \e43a; +$fa-var-steam-symbol: \f3f6; + +$fa-icons: ( + "0": $fa-var-0, + "1": $fa-var-1, + "2": $fa-var-2, + "3": $fa-var-3, + "4": $fa-var-4, + "5": $fa-var-5, + "6": $fa-var-6, + "7": $fa-var-7, + "8": $fa-var-8, + "9": $fa-var-9, + "fill-drip": $fa-var-fill-drip, + "arrows-to-circle": $fa-var-arrows-to-circle, + "circle-chevron-right": $fa-var-circle-chevron-right, + "chevron-circle-right": $fa-var-chevron-circle-right, + "at": $fa-var-at, + "trash-can": $fa-var-trash-can, + "trash-alt": $fa-var-trash-alt, + "text-height": $fa-var-text-height, + "user-xmark": $fa-var-user-xmark, + "user-times": $fa-var-user-times, + "stethoscope": $fa-var-stethoscope, + "message": $fa-var-message, + "comment-alt": $fa-var-comment-alt, + "info": $fa-var-info, + "down-left-and-up-right-to-center": $fa-var-down-left-and-up-right-to-center, + "compress-alt": $fa-var-compress-alt, + "explosion": $fa-var-explosion, + "file-lines": $fa-var-file-lines, + "file-alt": $fa-var-file-alt, + "file-text": $fa-var-file-text, + "wave-square": $fa-var-wave-square, + "ring": $fa-var-ring, + "building-un": $fa-var-building-un, + "dice-three": $fa-var-dice-three, + "calendar-days": $fa-var-calendar-days, + "calendar-alt": $fa-var-calendar-alt, + "anchor-circle-check": $fa-var-anchor-circle-check, + "building-circle-arrow-right": $fa-var-building-circle-arrow-right, + "volleyball": $fa-var-volleyball, + "volleyball-ball": $fa-var-volleyball-ball, + "arrows-up-to-line": $fa-var-arrows-up-to-line, + "sort-down": $fa-var-sort-down, + "sort-desc": $fa-var-sort-desc, + "circle-minus": $fa-var-circle-minus, + "minus-circle": $fa-var-minus-circle, + "door-open": $fa-var-door-open, + "right-from-bracket": $fa-var-right-from-bracket, + "sign-out-alt": $fa-var-sign-out-alt, + "atom": $fa-var-atom, + "soap": $fa-var-soap, + "icons": $fa-var-icons, + "heart-music-camera-bolt": $fa-var-heart-music-camera-bolt, + "microphone-lines-slash": $fa-var-microphone-lines-slash, + "microphone-alt-slash": $fa-var-microphone-alt-slash, + "bridge-circle-check": $fa-var-bridge-circle-check, + "pump-medical": $fa-var-pump-medical, + "fingerprint": $fa-var-fingerprint, + "hand-point-right": $fa-var-hand-point-right, + "magnifying-glass-location": $fa-var-magnifying-glass-location, + "search-location": $fa-var-search-location, + "forward-step": $fa-var-forward-step, + "step-forward": $fa-var-step-forward, + "face-smile-beam": $fa-var-face-smile-beam, + "smile-beam": $fa-var-smile-beam, + "flag-checkered": $fa-var-flag-checkered, + "football": $fa-var-football, + "football-ball": $fa-var-football-ball, + "school-circle-exclamation": $fa-var-school-circle-exclamation, + "crop": $fa-var-crop, + "angles-down": $fa-var-angles-down, + "angle-double-down": $fa-var-angle-double-down, + "users-rectangle": $fa-var-users-rectangle, + "people-roof": $fa-var-people-roof, + "people-line": $fa-var-people-line, + "beer-mug-empty": $fa-var-beer-mug-empty, + "beer": $fa-var-beer, + "diagram-predecessor": $fa-var-diagram-predecessor, + "arrow-up-long": $fa-var-arrow-up-long, + "long-arrow-up": $fa-var-long-arrow-up, + "fire-flame-simple": $fa-var-fire-flame-simple, + "burn": $fa-var-burn, + "person": $fa-var-person, + "male": $fa-var-male, + "laptop": $fa-var-laptop, + "file-csv": $fa-var-file-csv, + "menorah": $fa-var-menorah, + "truck-plane": $fa-var-truck-plane, + "record-vinyl": $fa-var-record-vinyl, + "face-grin-stars": $fa-var-face-grin-stars, + "grin-stars": $fa-var-grin-stars, + "bong": $fa-var-bong, + "spaghetti-monster-flying": $fa-var-spaghetti-monster-flying, + "pastafarianism": $fa-var-pastafarianism, + "arrow-down-up-across-line": $fa-var-arrow-down-up-across-line, + "spoon": $fa-var-spoon, + "utensil-spoon": $fa-var-utensil-spoon, + "jar-wheat": $fa-var-jar-wheat, + "envelopes-bulk": $fa-var-envelopes-bulk, + "mail-bulk": $fa-var-mail-bulk, + "file-circle-exclamation": $fa-var-file-circle-exclamation, + "circle-h": $fa-var-circle-h, + "hospital-symbol": $fa-var-hospital-symbol, + "pager": $fa-var-pager, + "address-book": $fa-var-address-book, + "contact-book": $fa-var-contact-book, + "strikethrough": $fa-var-strikethrough, + "k": $fa-var-k, + "landmark-flag": $fa-var-landmark-flag, + "pencil": $fa-var-pencil, + "pencil-alt": $fa-var-pencil-alt, + "backward": $fa-var-backward, + "caret-right": $fa-var-caret-right, + "comments": $fa-var-comments, + "paste": $fa-var-paste, + "file-clipboard": $fa-var-file-clipboard, + "code-pull-request": $fa-var-code-pull-request, + "clipboard-list": $fa-var-clipboard-list, + "truck-ramp-box": $fa-var-truck-ramp-box, + "truck-loading": $fa-var-truck-loading, + "user-check": $fa-var-user-check, + "vial-virus": $fa-var-vial-virus, + "sheet-plastic": $fa-var-sheet-plastic, + "blog": $fa-var-blog, + "user-ninja": $fa-var-user-ninja, + "person-arrow-up-from-line": $fa-var-person-arrow-up-from-line, + "scroll-torah": $fa-var-scroll-torah, + "torah": $fa-var-torah, + "broom-ball": $fa-var-broom-ball, + "quidditch": $fa-var-quidditch, + "quidditch-broom-ball": $fa-var-quidditch-broom-ball, + "toggle-off": $fa-var-toggle-off, + "box-archive": $fa-var-box-archive, + "archive": $fa-var-archive, + "person-drowning": $fa-var-person-drowning, + "arrow-down-9-1": $fa-var-arrow-down-9-1, + "sort-numeric-desc": $fa-var-sort-numeric-desc, + "sort-numeric-down-alt": $fa-var-sort-numeric-down-alt, + "face-grin-tongue-squint": $fa-var-face-grin-tongue-squint, + "grin-tongue-squint": $fa-var-grin-tongue-squint, + "spray-can": $fa-var-spray-can, + "truck-monster": $fa-var-truck-monster, + "w": $fa-var-w, + "earth-africa": $fa-var-earth-africa, + "globe-africa": $fa-var-globe-africa, + "rainbow": $fa-var-rainbow, + "circle-notch": $fa-var-circle-notch, + "tablet-screen-button": $fa-var-tablet-screen-button, + "tablet-alt": $fa-var-tablet-alt, + "paw": $fa-var-paw, + "cloud": $fa-var-cloud, + "trowel-bricks": $fa-var-trowel-bricks, + "face-flushed": $fa-var-face-flushed, + "flushed": $fa-var-flushed, + "hospital-user": $fa-var-hospital-user, + "tent-arrow-left-right": $fa-var-tent-arrow-left-right, + "gavel": $fa-var-gavel, + "legal": $fa-var-legal, + "binoculars": $fa-var-binoculars, + "microphone-slash": $fa-var-microphone-slash, + "box-tissue": $fa-var-box-tissue, + "motorcycle": $fa-var-motorcycle, + "bell-concierge": $fa-var-bell-concierge, + "concierge-bell": $fa-var-concierge-bell, + "pen-ruler": $fa-var-pen-ruler, + "pencil-ruler": $fa-var-pencil-ruler, + "people-arrows": $fa-var-people-arrows, + "people-arrows-left-right": $fa-var-people-arrows-left-right, + "mars-and-venus-burst": $fa-var-mars-and-venus-burst, + "square-caret-right": $fa-var-square-caret-right, + "caret-square-right": $fa-var-caret-square-right, + "scissors": $fa-var-scissors, + "cut": $fa-var-cut, + "sun-plant-wilt": $fa-var-sun-plant-wilt, + "toilets-portable": $fa-var-toilets-portable, + "hockey-puck": $fa-var-hockey-puck, + "table": $fa-var-table, + "magnifying-glass-arrow-right": $fa-var-magnifying-glass-arrow-right, + "tachograph-digital": $fa-var-tachograph-digital, + "digital-tachograph": $fa-var-digital-tachograph, + "users-slash": $fa-var-users-slash, + "clover": $fa-var-clover, + "reply": $fa-var-reply, + "mail-reply": $fa-var-mail-reply, + "star-and-crescent": $fa-var-star-and-crescent, + "house-fire": $fa-var-house-fire, + "square-minus": $fa-var-square-minus, + "minus-square": $fa-var-minus-square, + "helicopter": $fa-var-helicopter, + "compass": $fa-var-compass, + "square-caret-down": $fa-var-square-caret-down, + "caret-square-down": $fa-var-caret-square-down, + "file-circle-question": $fa-var-file-circle-question, + "laptop-code": $fa-var-laptop-code, + "swatchbook": $fa-var-swatchbook, + "prescription-bottle": $fa-var-prescription-bottle, + "bars": $fa-var-bars, + "navicon": $fa-var-navicon, + "people-group": $fa-var-people-group, + "hourglass-end": $fa-var-hourglass-end, + "hourglass-3": $fa-var-hourglass-3, + "heart-crack": $fa-var-heart-crack, + "heart-broken": $fa-var-heart-broken, + "square-up-right": $fa-var-square-up-right, + "external-link-square-alt": $fa-var-external-link-square-alt, + "face-kiss-beam": $fa-var-face-kiss-beam, + "kiss-beam": $fa-var-kiss-beam, + "film": $fa-var-film, + "ruler-horizontal": $fa-var-ruler-horizontal, + "people-robbery": $fa-var-people-robbery, + "lightbulb": $fa-var-lightbulb, + "caret-left": $fa-var-caret-left, + "circle-exclamation": $fa-var-circle-exclamation, + "exclamation-circle": $fa-var-exclamation-circle, + "school-circle-xmark": $fa-var-school-circle-xmark, + "arrow-right-from-bracket": $fa-var-arrow-right-from-bracket, + "sign-out": $fa-var-sign-out, + "circle-chevron-down": $fa-var-circle-chevron-down, + "chevron-circle-down": $fa-var-chevron-circle-down, + "unlock-keyhole": $fa-var-unlock-keyhole, + "unlock-alt": $fa-var-unlock-alt, + "cloud-showers-heavy": $fa-var-cloud-showers-heavy, + "headphones-simple": $fa-var-headphones-simple, + "headphones-alt": $fa-var-headphones-alt, + "sitemap": $fa-var-sitemap, + "circle-dollar-to-slot": $fa-var-circle-dollar-to-slot, + "donate": $fa-var-donate, + "memory": $fa-var-memory, + "road-spikes": $fa-var-road-spikes, + "fire-burner": $fa-var-fire-burner, + "flag": $fa-var-flag, + "hanukiah": $fa-var-hanukiah, + "feather": $fa-var-feather, + "volume-low": $fa-var-volume-low, + "volume-down": $fa-var-volume-down, + "comment-slash": $fa-var-comment-slash, + "cloud-sun-rain": $fa-var-cloud-sun-rain, + "compress": $fa-var-compress, + "wheat-awn": $fa-var-wheat-awn, + "wheat-alt": $fa-var-wheat-alt, + "ankh": $fa-var-ankh, + "hands-holding-child": $fa-var-hands-holding-child, + "asterisk": $fa-var-asterisk, + "square-check": $fa-var-square-check, + "check-square": $fa-var-check-square, + "peseta-sign": $fa-var-peseta-sign, + "heading": $fa-var-heading, + "header": $fa-var-header, + "ghost": $fa-var-ghost, + "list": $fa-var-list, + "list-squares": $fa-var-list-squares, + "square-phone-flip": $fa-var-square-phone-flip, + "phone-square-alt": $fa-var-phone-square-alt, + "cart-plus": $fa-var-cart-plus, + "gamepad": $fa-var-gamepad, + "circle-dot": $fa-var-circle-dot, + "dot-circle": $fa-var-dot-circle, + "face-dizzy": $fa-var-face-dizzy, + "dizzy": $fa-var-dizzy, + "egg": $fa-var-egg, + "house-medical-circle-xmark": $fa-var-house-medical-circle-xmark, + "campground": $fa-var-campground, + "folder-plus": $fa-var-folder-plus, + "futbol": $fa-var-futbol, + "futbol-ball": $fa-var-futbol-ball, + "soccer-ball": $fa-var-soccer-ball, + "paintbrush": $fa-var-paintbrush, + "paint-brush": $fa-var-paint-brush, + "lock": $fa-var-lock, + "gas-pump": $fa-var-gas-pump, + "hot-tub-person": $fa-var-hot-tub-person, + "hot-tub": $fa-var-hot-tub, + "map-location": $fa-var-map-location, + "map-marked": $fa-var-map-marked, + "house-flood-water": $fa-var-house-flood-water, + "tree": $fa-var-tree, + "bridge-lock": $fa-var-bridge-lock, + "sack-dollar": $fa-var-sack-dollar, + "pen-to-square": $fa-var-pen-to-square, + "edit": $fa-var-edit, + "car-side": $fa-var-car-side, + "share-nodes": $fa-var-share-nodes, + "share-alt": $fa-var-share-alt, + "heart-circle-minus": $fa-var-heart-circle-minus, + "hourglass-half": $fa-var-hourglass-half, + "hourglass-2": $fa-var-hourglass-2, + "microscope": $fa-var-microscope, + "sink": $fa-var-sink, + "bag-shopping": $fa-var-bag-shopping, + "shopping-bag": $fa-var-shopping-bag, + "arrow-down-z-a": $fa-var-arrow-down-z-a, + "sort-alpha-desc": $fa-var-sort-alpha-desc, + "sort-alpha-down-alt": $fa-var-sort-alpha-down-alt, + "mitten": $fa-var-mitten, + "person-rays": $fa-var-person-rays, + "users": $fa-var-users, + "eye-slash": $fa-var-eye-slash, + "flask-vial": $fa-var-flask-vial, + "hand": $fa-var-hand, + "hand-paper": $fa-var-hand-paper, + "om": $fa-var-om, + "worm": $fa-var-worm, + "house-circle-xmark": $fa-var-house-circle-xmark, + "plug": $fa-var-plug, + "chevron-up": $fa-var-chevron-up, + "hand-spock": $fa-var-hand-spock, + "stopwatch": $fa-var-stopwatch, + "face-kiss": $fa-var-face-kiss, + "kiss": $fa-var-kiss, + "bridge-circle-xmark": $fa-var-bridge-circle-xmark, + "face-grin-tongue": $fa-var-face-grin-tongue, + "grin-tongue": $fa-var-grin-tongue, + "chess-bishop": $fa-var-chess-bishop, + "face-grin-wink": $fa-var-face-grin-wink, + "grin-wink": $fa-var-grin-wink, + "ear-deaf": $fa-var-ear-deaf, + "deaf": $fa-var-deaf, + "deafness": $fa-var-deafness, + "hard-of-hearing": $fa-var-hard-of-hearing, + "road-circle-check": $fa-var-road-circle-check, + "dice-five": $fa-var-dice-five, + "square-rss": $fa-var-square-rss, + "rss-square": $fa-var-rss-square, + "land-mine-on": $fa-var-land-mine-on, + "i-cursor": $fa-var-i-cursor, + "stamp": $fa-var-stamp, + "stairs": $fa-var-stairs, + "i": $fa-var-i, + "hryvnia-sign": $fa-var-hryvnia-sign, + "hryvnia": $fa-var-hryvnia, + "pills": $fa-var-pills, + "face-grin-wide": $fa-var-face-grin-wide, + "grin-alt": $fa-var-grin-alt, + "tooth": $fa-var-tooth, + "v": $fa-var-v, + "bangladeshi-taka-sign": $fa-var-bangladeshi-taka-sign, + "bicycle": $fa-var-bicycle, + "staff-snake": $fa-var-staff-snake, + "rod-asclepius": $fa-var-rod-asclepius, + "rod-snake": $fa-var-rod-snake, + "staff-aesculapius": $fa-var-staff-aesculapius, + "head-side-cough-slash": $fa-var-head-side-cough-slash, + "truck-medical": $fa-var-truck-medical, + "ambulance": $fa-var-ambulance, + "wheat-awn-circle-exclamation": $fa-var-wheat-awn-circle-exclamation, + "snowman": $fa-var-snowman, + "mortar-pestle": $fa-var-mortar-pestle, + "road-barrier": $fa-var-road-barrier, + "school": $fa-var-school, + "igloo": $fa-var-igloo, + "joint": $fa-var-joint, + "angle-right": $fa-var-angle-right, + "horse": $fa-var-horse, + "q": $fa-var-q, + "g": $fa-var-g, + "notes-medical": $fa-var-notes-medical, + "temperature-half": $fa-var-temperature-half, + "temperature-2": $fa-var-temperature-2, + "thermometer-2": $fa-var-thermometer-2, + "thermometer-half": $fa-var-thermometer-half, + "dong-sign": $fa-var-dong-sign, + "capsules": $fa-var-capsules, + "poo-storm": $fa-var-poo-storm, + "poo-bolt": $fa-var-poo-bolt, + "face-frown-open": $fa-var-face-frown-open, + "frown-open": $fa-var-frown-open, + "hand-point-up": $fa-var-hand-point-up, + "money-bill": $fa-var-money-bill, + "bookmark": $fa-var-bookmark, + "align-justify": $fa-var-align-justify, + "umbrella-beach": $fa-var-umbrella-beach, + "helmet-un": $fa-var-helmet-un, + "bullseye": $fa-var-bullseye, + "bacon": $fa-var-bacon, + "hand-point-down": $fa-var-hand-point-down, + "arrow-up-from-bracket": $fa-var-arrow-up-from-bracket, + "folder": $fa-var-folder, + "folder-blank": $fa-var-folder-blank, + "file-waveform": $fa-var-file-waveform, + "file-medical-alt": $fa-var-file-medical-alt, + "radiation": $fa-var-radiation, + "chart-simple": $fa-var-chart-simple, + "mars-stroke": $fa-var-mars-stroke, + "vial": $fa-var-vial, + "gauge": $fa-var-gauge, + "dashboard": $fa-var-dashboard, + "gauge-med": $fa-var-gauge-med, + "tachometer-alt-average": $fa-var-tachometer-alt-average, + "wand-magic-sparkles": $fa-var-wand-magic-sparkles, + "magic-wand-sparkles": $fa-var-magic-wand-sparkles, + "e": $fa-var-e, + "pen-clip": $fa-var-pen-clip, + "pen-alt": $fa-var-pen-alt, + "bridge-circle-exclamation": $fa-var-bridge-circle-exclamation, + "user": $fa-var-user, + "school-circle-check": $fa-var-school-circle-check, + "dumpster": $fa-var-dumpster, + "van-shuttle": $fa-var-van-shuttle, + "shuttle-van": $fa-var-shuttle-van, + "building-user": $fa-var-building-user, + "square-caret-left": $fa-var-square-caret-left, + "caret-square-left": $fa-var-caret-square-left, + "highlighter": $fa-var-highlighter, + "key": $fa-var-key, + "bullhorn": $fa-var-bullhorn, + "globe": $fa-var-globe, + "synagogue": $fa-var-synagogue, + "person-half-dress": $fa-var-person-half-dress, + "road-bridge": $fa-var-road-bridge, + "location-arrow": $fa-var-location-arrow, + "c": $fa-var-c, + "tablet-button": $fa-var-tablet-button, + "building-lock": $fa-var-building-lock, + "pizza-slice": $fa-var-pizza-slice, + "money-bill-wave": $fa-var-money-bill-wave, + "chart-area": $fa-var-chart-area, + "area-chart": $fa-var-area-chart, + "house-flag": $fa-var-house-flag, + "person-circle-minus": $fa-var-person-circle-minus, + "ban": $fa-var-ban, + "cancel": $fa-var-cancel, + "camera-rotate": $fa-var-camera-rotate, + "spray-can-sparkles": $fa-var-spray-can-sparkles, + "air-freshener": $fa-var-air-freshener, + "star": $fa-var-star, + "repeat": $fa-var-repeat, + "cross": $fa-var-cross, + "box": $fa-var-box, + "venus-mars": $fa-var-venus-mars, + "arrow-pointer": $fa-var-arrow-pointer, + "mouse-pointer": $fa-var-mouse-pointer, + "maximize": $fa-var-maximize, + "expand-arrows-alt": $fa-var-expand-arrows-alt, + "charging-station": $fa-var-charging-station, + "shapes": $fa-var-shapes, + "triangle-circle-square": $fa-var-triangle-circle-square, + "shuffle": $fa-var-shuffle, + "random": $fa-var-random, + "person-running": $fa-var-person-running, + "running": $fa-var-running, + "mobile-retro": $fa-var-mobile-retro, + "grip-lines-vertical": $fa-var-grip-lines-vertical, + "spider": $fa-var-spider, + "hands-bound": $fa-var-hands-bound, + "file-invoice-dollar": $fa-var-file-invoice-dollar, + "plane-circle-exclamation": $fa-var-plane-circle-exclamation, + "x-ray": $fa-var-x-ray, + "spell-check": $fa-var-spell-check, + "slash": $fa-var-slash, + "computer-mouse": $fa-var-computer-mouse, + "mouse": $fa-var-mouse, + "arrow-right-to-bracket": $fa-var-arrow-right-to-bracket, + "sign-in": $fa-var-sign-in, + "shop-slash": $fa-var-shop-slash, + "store-alt-slash": $fa-var-store-alt-slash, + "server": $fa-var-server, + "virus-covid-slash": $fa-var-virus-covid-slash, + "shop-lock": $fa-var-shop-lock, + "hourglass-start": $fa-var-hourglass-start, + "hourglass-1": $fa-var-hourglass-1, + "blender-phone": $fa-var-blender-phone, + "building-wheat": $fa-var-building-wheat, + "person-breastfeeding": $fa-var-person-breastfeeding, + "right-to-bracket": $fa-var-right-to-bracket, + "sign-in-alt": $fa-var-sign-in-alt, + "venus": $fa-var-venus, + "passport": $fa-var-passport, + "heart-pulse": $fa-var-heart-pulse, + "heartbeat": $fa-var-heartbeat, + "people-carry-box": $fa-var-people-carry-box, + "people-carry": $fa-var-people-carry, + "temperature-high": $fa-var-temperature-high, + "microchip": $fa-var-microchip, + "crown": $fa-var-crown, + "weight-hanging": $fa-var-weight-hanging, + "xmarks-lines": $fa-var-xmarks-lines, + "file-prescription": $fa-var-file-prescription, + "weight-scale": $fa-var-weight-scale, + "weight": $fa-var-weight, + "user-group": $fa-var-user-group, + "user-friends": $fa-var-user-friends, + "arrow-up-a-z": $fa-var-arrow-up-a-z, + "sort-alpha-up": $fa-var-sort-alpha-up, + "chess-knight": $fa-var-chess-knight, + "face-laugh-squint": $fa-var-face-laugh-squint, + "laugh-squint": $fa-var-laugh-squint, + "wheelchair": $fa-var-wheelchair, + "circle-arrow-up": $fa-var-circle-arrow-up, + "arrow-circle-up": $fa-var-arrow-circle-up, + "toggle-on": $fa-var-toggle-on, + "person-walking": $fa-var-person-walking, + "walking": $fa-var-walking, + "l": $fa-var-l, + "fire": $fa-var-fire, + "bed-pulse": $fa-var-bed-pulse, + "procedures": $fa-var-procedures, + "shuttle-space": $fa-var-shuttle-space, + "space-shuttle": $fa-var-space-shuttle, + "face-laugh": $fa-var-face-laugh, + "laugh": $fa-var-laugh, + "folder-open": $fa-var-folder-open, + "heart-circle-plus": $fa-var-heart-circle-plus, + "code-fork": $fa-var-code-fork, + "city": $fa-var-city, + "microphone-lines": $fa-var-microphone-lines, + "microphone-alt": $fa-var-microphone-alt, + "pepper-hot": $fa-var-pepper-hot, + "unlock": $fa-var-unlock, + "colon-sign": $fa-var-colon-sign, + "headset": $fa-var-headset, + "store-slash": $fa-var-store-slash, + "road-circle-xmark": $fa-var-road-circle-xmark, + "user-minus": $fa-var-user-minus, + "mars-stroke-up": $fa-var-mars-stroke-up, + "mars-stroke-v": $fa-var-mars-stroke-v, + "champagne-glasses": $fa-var-champagne-glasses, + "glass-cheers": $fa-var-glass-cheers, + "clipboard": $fa-var-clipboard, + "house-circle-exclamation": $fa-var-house-circle-exclamation, + "file-arrow-up": $fa-var-file-arrow-up, + "file-upload": $fa-var-file-upload, + "wifi": $fa-var-wifi, + "wifi-3": $fa-var-wifi-3, + "wifi-strong": $fa-var-wifi-strong, + "bath": $fa-var-bath, + "bathtub": $fa-var-bathtub, + "underline": $fa-var-underline, + "user-pen": $fa-var-user-pen, + "user-edit": $fa-var-user-edit, + "signature": $fa-var-signature, + "stroopwafel": $fa-var-stroopwafel, + "bold": $fa-var-bold, + "anchor-lock": $fa-var-anchor-lock, + "building-ngo": $fa-var-building-ngo, + "manat-sign": $fa-var-manat-sign, + "not-equal": $fa-var-not-equal, + "border-top-left": $fa-var-border-top-left, + "border-style": $fa-var-border-style, + "map-location-dot": $fa-var-map-location-dot, + "map-marked-alt": $fa-var-map-marked-alt, + "jedi": $fa-var-jedi, + "square-poll-vertical": $fa-var-square-poll-vertical, + "poll": $fa-var-poll, + "mug-hot": $fa-var-mug-hot, + "car-battery": $fa-var-car-battery, + "battery-car": $fa-var-battery-car, + "gift": $fa-var-gift, + "dice-two": $fa-var-dice-two, + "chess-queen": $fa-var-chess-queen, + "glasses": $fa-var-glasses, + "chess-board": $fa-var-chess-board, + "building-circle-check": $fa-var-building-circle-check, + "person-chalkboard": $fa-var-person-chalkboard, + "mars-stroke-right": $fa-var-mars-stroke-right, + "mars-stroke-h": $fa-var-mars-stroke-h, + "hand-back-fist": $fa-var-hand-back-fist, + "hand-rock": $fa-var-hand-rock, + "square-caret-up": $fa-var-square-caret-up, + "caret-square-up": $fa-var-caret-square-up, + "cloud-showers-water": $fa-var-cloud-showers-water, + "chart-bar": $fa-var-chart-bar, + "bar-chart": $fa-var-bar-chart, + "hands-bubbles": $fa-var-hands-bubbles, + "hands-wash": $fa-var-hands-wash, + "less-than-equal": $fa-var-less-than-equal, + "train": $fa-var-train, + "eye-low-vision": $fa-var-eye-low-vision, + "low-vision": $fa-var-low-vision, + "crow": $fa-var-crow, + "sailboat": $fa-var-sailboat, + "window-restore": $fa-var-window-restore, + "square-plus": $fa-var-square-plus, + "plus-square": $fa-var-plus-square, + "torii-gate": $fa-var-torii-gate, + "frog": $fa-var-frog, + "bucket": $fa-var-bucket, + "image": $fa-var-image, + "microphone": $fa-var-microphone, + "cow": $fa-var-cow, + "caret-up": $fa-var-caret-up, + "screwdriver": $fa-var-screwdriver, + "folder-closed": $fa-var-folder-closed, + "house-tsunami": $fa-var-house-tsunami, + "square-nfi": $fa-var-square-nfi, + "arrow-up-from-ground-water": $fa-var-arrow-up-from-ground-water, + "martini-glass": $fa-var-martini-glass, + "glass-martini-alt": $fa-var-glass-martini-alt, + "rotate-left": $fa-var-rotate-left, + "rotate-back": $fa-var-rotate-back, + "rotate-backward": $fa-var-rotate-backward, + "undo-alt": $fa-var-undo-alt, + "table-columns": $fa-var-table-columns, + "columns": $fa-var-columns, + "lemon": $fa-var-lemon, + "head-side-mask": $fa-var-head-side-mask, + "handshake": $fa-var-handshake, + "gem": $fa-var-gem, + "dolly": $fa-var-dolly, + "dolly-box": $fa-var-dolly-box, + "smoking": $fa-var-smoking, + "minimize": $fa-var-minimize, + "compress-arrows-alt": $fa-var-compress-arrows-alt, + "monument": $fa-var-monument, + "snowplow": $fa-var-snowplow, + "angles-right": $fa-var-angles-right, + "angle-double-right": $fa-var-angle-double-right, + "cannabis": $fa-var-cannabis, + "circle-play": $fa-var-circle-play, + "play-circle": $fa-var-play-circle, + "tablets": $fa-var-tablets, + "ethernet": $fa-var-ethernet, + "euro-sign": $fa-var-euro-sign, + "eur": $fa-var-eur, + "euro": $fa-var-euro, + "chair": $fa-var-chair, + "circle-check": $fa-var-circle-check, + "check-circle": $fa-var-check-circle, + "circle-stop": $fa-var-circle-stop, + "stop-circle": $fa-var-stop-circle, + "compass-drafting": $fa-var-compass-drafting, + "drafting-compass": $fa-var-drafting-compass, + "plate-wheat": $fa-var-plate-wheat, + "icicles": $fa-var-icicles, + "person-shelter": $fa-var-person-shelter, + "neuter": $fa-var-neuter, + "id-badge": $fa-var-id-badge, + "marker": $fa-var-marker, + "face-laugh-beam": $fa-var-face-laugh-beam, + "laugh-beam": $fa-var-laugh-beam, + "helicopter-symbol": $fa-var-helicopter-symbol, + "universal-access": $fa-var-universal-access, + "circle-chevron-up": $fa-var-circle-chevron-up, + "chevron-circle-up": $fa-var-chevron-circle-up, + "lari-sign": $fa-var-lari-sign, + "volcano": $fa-var-volcano, + "person-walking-dashed-line-arrow-right": $fa-var-person-walking-dashed-line-arrow-right, + "sterling-sign": $fa-var-sterling-sign, + "gbp": $fa-var-gbp, + "pound-sign": $fa-var-pound-sign, + "viruses": $fa-var-viruses, + "square-person-confined": $fa-var-square-person-confined, + "user-tie": $fa-var-user-tie, + "arrow-down-long": $fa-var-arrow-down-long, + "long-arrow-down": $fa-var-long-arrow-down, + "tent-arrow-down-to-line": $fa-var-tent-arrow-down-to-line, + "certificate": $fa-var-certificate, + "reply-all": $fa-var-reply-all, + "mail-reply-all": $fa-var-mail-reply-all, + "suitcase": $fa-var-suitcase, + "person-skating": $fa-var-person-skating, + "skating": $fa-var-skating, + "filter-circle-dollar": $fa-var-filter-circle-dollar, + "funnel-dollar": $fa-var-funnel-dollar, + "camera-retro": $fa-var-camera-retro, + "circle-arrow-down": $fa-var-circle-arrow-down, + "arrow-circle-down": $fa-var-arrow-circle-down, + "file-import": $fa-var-file-import, + "arrow-right-to-file": $fa-var-arrow-right-to-file, + "square-arrow-up-right": $fa-var-square-arrow-up-right, + "external-link-square": $fa-var-external-link-square, + "box-open": $fa-var-box-open, + "scroll": $fa-var-scroll, + "spa": $fa-var-spa, + "location-pin-lock": $fa-var-location-pin-lock, + "pause": $fa-var-pause, + "hill-avalanche": $fa-var-hill-avalanche, + "temperature-empty": $fa-var-temperature-empty, + "temperature-0": $fa-var-temperature-0, + "thermometer-0": $fa-var-thermometer-0, + "thermometer-empty": $fa-var-thermometer-empty, + "bomb": $fa-var-bomb, + "registered": $fa-var-registered, + "address-card": $fa-var-address-card, + "contact-card": $fa-var-contact-card, + "vcard": $fa-var-vcard, + "scale-unbalanced-flip": $fa-var-scale-unbalanced-flip, + "balance-scale-right": $fa-var-balance-scale-right, + "subscript": $fa-var-subscript, + "diamond-turn-right": $fa-var-diamond-turn-right, + "directions": $fa-var-directions, + "burst": $fa-var-burst, + "house-laptop": $fa-var-house-laptop, + "laptop-house": $fa-var-laptop-house, + "face-tired": $fa-var-face-tired, + "tired": $fa-var-tired, + "money-bills": $fa-var-money-bills, + "smog": $fa-var-smog, + "crutch": $fa-var-crutch, + "cloud-arrow-up": $fa-var-cloud-arrow-up, + "cloud-upload": $fa-var-cloud-upload, + "cloud-upload-alt": $fa-var-cloud-upload-alt, + "palette": $fa-var-palette, + "arrows-turn-right": $fa-var-arrows-turn-right, + "vest": $fa-var-vest, + "ferry": $fa-var-ferry, + "arrows-down-to-people": $fa-var-arrows-down-to-people, + "seedling": $fa-var-seedling, + "sprout": $fa-var-sprout, + "left-right": $fa-var-left-right, + "arrows-alt-h": $fa-var-arrows-alt-h, + "boxes-packing": $fa-var-boxes-packing, + "circle-arrow-left": $fa-var-circle-arrow-left, + "arrow-circle-left": $fa-var-arrow-circle-left, + "group-arrows-rotate": $fa-var-group-arrows-rotate, + "bowl-food": $fa-var-bowl-food, + "candy-cane": $fa-var-candy-cane, + "arrow-down-wide-short": $fa-var-arrow-down-wide-short, + "sort-amount-asc": $fa-var-sort-amount-asc, + "sort-amount-down": $fa-var-sort-amount-down, + "cloud-bolt": $fa-var-cloud-bolt, + "thunderstorm": $fa-var-thunderstorm, + "text-slash": $fa-var-text-slash, + "remove-format": $fa-var-remove-format, + "face-smile-wink": $fa-var-face-smile-wink, + "smile-wink": $fa-var-smile-wink, + "file-word": $fa-var-file-word, + "file-powerpoint": $fa-var-file-powerpoint, + "arrows-left-right": $fa-var-arrows-left-right, + "arrows-h": $fa-var-arrows-h, + "house-lock": $fa-var-house-lock, + "cloud-arrow-down": $fa-var-cloud-arrow-down, + "cloud-download": $fa-var-cloud-download, + "cloud-download-alt": $fa-var-cloud-download-alt, + "children": $fa-var-children, + "chalkboard": $fa-var-chalkboard, + "blackboard": $fa-var-blackboard, + "user-large-slash": $fa-var-user-large-slash, + "user-alt-slash": $fa-var-user-alt-slash, + "envelope-open": $fa-var-envelope-open, + "handshake-simple-slash": $fa-var-handshake-simple-slash, + "handshake-alt-slash": $fa-var-handshake-alt-slash, + "mattress-pillow": $fa-var-mattress-pillow, + "guarani-sign": $fa-var-guarani-sign, + "arrows-rotate": $fa-var-arrows-rotate, + "refresh": $fa-var-refresh, + "sync": $fa-var-sync, + "fire-extinguisher": $fa-var-fire-extinguisher, + "cruzeiro-sign": $fa-var-cruzeiro-sign, + "greater-than-equal": $fa-var-greater-than-equal, + "shield-halved": $fa-var-shield-halved, + "shield-alt": $fa-var-shield-alt, + "book-atlas": $fa-var-book-atlas, + "atlas": $fa-var-atlas, + "virus": $fa-var-virus, + "envelope-circle-check": $fa-var-envelope-circle-check, + "layer-group": $fa-var-layer-group, + "arrows-to-dot": $fa-var-arrows-to-dot, + "archway": $fa-var-archway, + "heart-circle-check": $fa-var-heart-circle-check, + "house-chimney-crack": $fa-var-house-chimney-crack, + "house-damage": $fa-var-house-damage, + "file-zipper": $fa-var-file-zipper, + "file-archive": $fa-var-file-archive, + "square": $fa-var-square, + "martini-glass-empty": $fa-var-martini-glass-empty, + "glass-martini": $fa-var-glass-martini, + "couch": $fa-var-couch, + "cedi-sign": $fa-var-cedi-sign, + "italic": $fa-var-italic, + "church": $fa-var-church, + "comments-dollar": $fa-var-comments-dollar, + "democrat": $fa-var-democrat, + "z": $fa-var-z, + "person-skiing": $fa-var-person-skiing, + "skiing": $fa-var-skiing, + "road-lock": $fa-var-road-lock, + "a": $fa-var-a, + "temperature-arrow-down": $fa-var-temperature-arrow-down, + "temperature-down": $fa-var-temperature-down, + "feather-pointed": $fa-var-feather-pointed, + "feather-alt": $fa-var-feather-alt, + "p": $fa-var-p, + "snowflake": $fa-var-snowflake, + "newspaper": $fa-var-newspaper, + "rectangle-ad": $fa-var-rectangle-ad, + "ad": $fa-var-ad, + "circle-arrow-right": $fa-var-circle-arrow-right, + "arrow-circle-right": $fa-var-arrow-circle-right, + "filter-circle-xmark": $fa-var-filter-circle-xmark, + "locust": $fa-var-locust, + "sort": $fa-var-sort, + "unsorted": $fa-var-unsorted, + "list-ol": $fa-var-list-ol, + "list-1-2": $fa-var-list-1-2, + "list-numeric": $fa-var-list-numeric, + "person-dress-burst": $fa-var-person-dress-burst, + "money-check-dollar": $fa-var-money-check-dollar, + "money-check-alt": $fa-var-money-check-alt, + "vector-square": $fa-var-vector-square, + "bread-slice": $fa-var-bread-slice, + "language": $fa-var-language, + "face-kiss-wink-heart": $fa-var-face-kiss-wink-heart, + "kiss-wink-heart": $fa-var-kiss-wink-heart, + "filter": $fa-var-filter, + "question": $fa-var-question, + "file-signature": $fa-var-file-signature, + "up-down-left-right": $fa-var-up-down-left-right, + "arrows-alt": $fa-var-arrows-alt, + "house-chimney-user": $fa-var-house-chimney-user, + "hand-holding-heart": $fa-var-hand-holding-heart, + "puzzle-piece": $fa-var-puzzle-piece, + "money-check": $fa-var-money-check, + "star-half-stroke": $fa-var-star-half-stroke, + "star-half-alt": $fa-var-star-half-alt, + "code": $fa-var-code, + "whiskey-glass": $fa-var-whiskey-glass, + "glass-whiskey": $fa-var-glass-whiskey, + "building-circle-exclamation": $fa-var-building-circle-exclamation, + "magnifying-glass-chart": $fa-var-magnifying-glass-chart, + "arrow-up-right-from-square": $fa-var-arrow-up-right-from-square, + "external-link": $fa-var-external-link, + "cubes-stacked": $fa-var-cubes-stacked, + "won-sign": $fa-var-won-sign, + "krw": $fa-var-krw, + "won": $fa-var-won, + "virus-covid": $fa-var-virus-covid, + "austral-sign": $fa-var-austral-sign, + "f": $fa-var-f, + "leaf": $fa-var-leaf, + "road": $fa-var-road, + "taxi": $fa-var-taxi, + "cab": $fa-var-cab, + "person-circle-plus": $fa-var-person-circle-plus, + "chart-pie": $fa-var-chart-pie, + "pie-chart": $fa-var-pie-chart, + "bolt-lightning": $fa-var-bolt-lightning, + "sack-xmark": $fa-var-sack-xmark, + "file-excel": $fa-var-file-excel, + "file-contract": $fa-var-file-contract, + "fish-fins": $fa-var-fish-fins, + "building-flag": $fa-var-building-flag, + "face-grin-beam": $fa-var-face-grin-beam, + "grin-beam": $fa-var-grin-beam, + "object-ungroup": $fa-var-object-ungroup, + "poop": $fa-var-poop, + "location-pin": $fa-var-location-pin, + "map-marker": $fa-var-map-marker, + "kaaba": $fa-var-kaaba, + "toilet-paper": $fa-var-toilet-paper, + "helmet-safety": $fa-var-helmet-safety, + "hard-hat": $fa-var-hard-hat, + "hat-hard": $fa-var-hat-hard, + "eject": $fa-var-eject, + "circle-right": $fa-var-circle-right, + "arrow-alt-circle-right": $fa-var-arrow-alt-circle-right, + "plane-circle-check": $fa-var-plane-circle-check, + "face-rolling-eyes": $fa-var-face-rolling-eyes, + "meh-rolling-eyes": $fa-var-meh-rolling-eyes, + "object-group": $fa-var-object-group, + "chart-line": $fa-var-chart-line, + "line-chart": $fa-var-line-chart, + "mask-ventilator": $fa-var-mask-ventilator, + "arrow-right": $fa-var-arrow-right, + "signs-post": $fa-var-signs-post, + "map-signs": $fa-var-map-signs, + "cash-register": $fa-var-cash-register, + "person-circle-question": $fa-var-person-circle-question, + "h": $fa-var-h, + "tarp": $fa-var-tarp, + "screwdriver-wrench": $fa-var-screwdriver-wrench, + "tools": $fa-var-tools, + "arrows-to-eye": $fa-var-arrows-to-eye, + "plug-circle-bolt": $fa-var-plug-circle-bolt, + "heart": $fa-var-heart, + "mars-and-venus": $fa-var-mars-and-venus, + "house-user": $fa-var-house-user, + "home-user": $fa-var-home-user, + "dumpster-fire": $fa-var-dumpster-fire, + "house-crack": $fa-var-house-crack, + "martini-glass-citrus": $fa-var-martini-glass-citrus, + "cocktail": $fa-var-cocktail, + "face-surprise": $fa-var-face-surprise, + "surprise": $fa-var-surprise, + "bottle-water": $fa-var-bottle-water, + "circle-pause": $fa-var-circle-pause, + "pause-circle": $fa-var-pause-circle, + "toilet-paper-slash": $fa-var-toilet-paper-slash, + "apple-whole": $fa-var-apple-whole, + "apple-alt": $fa-var-apple-alt, + "kitchen-set": $fa-var-kitchen-set, + "r": $fa-var-r, + "temperature-quarter": $fa-var-temperature-quarter, + "temperature-1": $fa-var-temperature-1, + "thermometer-1": $fa-var-thermometer-1, + "thermometer-quarter": $fa-var-thermometer-quarter, + "cube": $fa-var-cube, + "bitcoin-sign": $fa-var-bitcoin-sign, + "shield-dog": $fa-var-shield-dog, + "solar-panel": $fa-var-solar-panel, + "lock-open": $fa-var-lock-open, + "elevator": $fa-var-elevator, + "money-bill-transfer": $fa-var-money-bill-transfer, + "money-bill-trend-up": $fa-var-money-bill-trend-up, + "house-flood-water-circle-arrow-right": $fa-var-house-flood-water-circle-arrow-right, + "square-poll-horizontal": $fa-var-square-poll-horizontal, + "poll-h": $fa-var-poll-h, + "circle": $fa-var-circle, + "backward-fast": $fa-var-backward-fast, + "fast-backward": $fa-var-fast-backward, + "recycle": $fa-var-recycle, + "user-astronaut": $fa-var-user-astronaut, + "plane-slash": $fa-var-plane-slash, + "trademark": $fa-var-trademark, + "basketball": $fa-var-basketball, + "basketball-ball": $fa-var-basketball-ball, + "satellite-dish": $fa-var-satellite-dish, + "circle-up": $fa-var-circle-up, + "arrow-alt-circle-up": $fa-var-arrow-alt-circle-up, + "mobile-screen-button": $fa-var-mobile-screen-button, + "mobile-alt": $fa-var-mobile-alt, + "volume-high": $fa-var-volume-high, + "volume-up": $fa-var-volume-up, + "users-rays": $fa-var-users-rays, + "wallet": $fa-var-wallet, + "clipboard-check": $fa-var-clipboard-check, + "file-audio": $fa-var-file-audio, + "burger": $fa-var-burger, + "hamburger": $fa-var-hamburger, + "wrench": $fa-var-wrench, + "bugs": $fa-var-bugs, + "rupee-sign": $fa-var-rupee-sign, + "rupee": $fa-var-rupee, + "file-image": $fa-var-file-image, + "circle-question": $fa-var-circle-question, + "question-circle": $fa-var-question-circle, + "plane-departure": $fa-var-plane-departure, + "handshake-slash": $fa-var-handshake-slash, + "book-bookmark": $fa-var-book-bookmark, + "code-branch": $fa-var-code-branch, + "hat-cowboy": $fa-var-hat-cowboy, + "bridge": $fa-var-bridge, + "phone-flip": $fa-var-phone-flip, + "phone-alt": $fa-var-phone-alt, + "truck-front": $fa-var-truck-front, + "cat": $fa-var-cat, + "anchor-circle-exclamation": $fa-var-anchor-circle-exclamation, + "truck-field": $fa-var-truck-field, + "route": $fa-var-route, + "clipboard-question": $fa-var-clipboard-question, + "panorama": $fa-var-panorama, + "comment-medical": $fa-var-comment-medical, + "teeth-open": $fa-var-teeth-open, + "file-circle-minus": $fa-var-file-circle-minus, + "tags": $fa-var-tags, + "wine-glass": $fa-var-wine-glass, + "forward-fast": $fa-var-forward-fast, + "fast-forward": $fa-var-fast-forward, + "face-meh-blank": $fa-var-face-meh-blank, + "meh-blank": $fa-var-meh-blank, + "square-parking": $fa-var-square-parking, + "parking": $fa-var-parking, + "house-signal": $fa-var-house-signal, + "bars-progress": $fa-var-bars-progress, + "tasks-alt": $fa-var-tasks-alt, + "faucet-drip": $fa-var-faucet-drip, + "cart-flatbed": $fa-var-cart-flatbed, + "dolly-flatbed": $fa-var-dolly-flatbed, + "ban-smoking": $fa-var-ban-smoking, + "smoking-ban": $fa-var-smoking-ban, + "terminal": $fa-var-terminal, + "mobile-button": $fa-var-mobile-button, + "house-medical-flag": $fa-var-house-medical-flag, + "basket-shopping": $fa-var-basket-shopping, + "shopping-basket": $fa-var-shopping-basket, + "tape": $fa-var-tape, + "bus-simple": $fa-var-bus-simple, + "bus-alt": $fa-var-bus-alt, + "eye": $fa-var-eye, + "face-sad-cry": $fa-var-face-sad-cry, + "sad-cry": $fa-var-sad-cry, + "audio-description": $fa-var-audio-description, + "person-military-to-person": $fa-var-person-military-to-person, + "file-shield": $fa-var-file-shield, + "user-slash": $fa-var-user-slash, + "pen": $fa-var-pen, + "tower-observation": $fa-var-tower-observation, + "file-code": $fa-var-file-code, + "signal": $fa-var-signal, + "signal-5": $fa-var-signal-5, + "signal-perfect": $fa-var-signal-perfect, + "bus": $fa-var-bus, + "heart-circle-xmark": $fa-var-heart-circle-xmark, + "house-chimney": $fa-var-house-chimney, + "home-lg": $fa-var-home-lg, + "window-maximize": $fa-var-window-maximize, + "face-frown": $fa-var-face-frown, + "frown": $fa-var-frown, + "prescription": $fa-var-prescription, + "shop": $fa-var-shop, + "store-alt": $fa-var-store-alt, + "floppy-disk": $fa-var-floppy-disk, + "save": $fa-var-save, + "vihara": $fa-var-vihara, + "scale-unbalanced": $fa-var-scale-unbalanced, + "balance-scale-left": $fa-var-balance-scale-left, + "sort-up": $fa-var-sort-up, + "sort-asc": $fa-var-sort-asc, + "comment-dots": $fa-var-comment-dots, + "commenting": $fa-var-commenting, + "plant-wilt": $fa-var-plant-wilt, + "diamond": $fa-var-diamond, + "face-grin-squint": $fa-var-face-grin-squint, + "grin-squint": $fa-var-grin-squint, + "hand-holding-dollar": $fa-var-hand-holding-dollar, + "hand-holding-usd": $fa-var-hand-holding-usd, + "bacterium": $fa-var-bacterium, + "hand-pointer": $fa-var-hand-pointer, + "drum-steelpan": $fa-var-drum-steelpan, + "hand-scissors": $fa-var-hand-scissors, + "hands-praying": $fa-var-hands-praying, + "praying-hands": $fa-var-praying-hands, + "arrow-rotate-right": $fa-var-arrow-rotate-right, + "arrow-right-rotate": $fa-var-arrow-right-rotate, + "arrow-rotate-forward": $fa-var-arrow-rotate-forward, + "redo": $fa-var-redo, + "biohazard": $fa-var-biohazard, + "location-crosshairs": $fa-var-location-crosshairs, + "location": $fa-var-location, + "mars-double": $fa-var-mars-double, + "child-dress": $fa-var-child-dress, + "users-between-lines": $fa-var-users-between-lines, + "lungs-virus": $fa-var-lungs-virus, + "face-grin-tears": $fa-var-face-grin-tears, + "grin-tears": $fa-var-grin-tears, + "phone": $fa-var-phone, + "calendar-xmark": $fa-var-calendar-xmark, + "calendar-times": $fa-var-calendar-times, + "child-reaching": $fa-var-child-reaching, + "head-side-virus": $fa-var-head-side-virus, + "user-gear": $fa-var-user-gear, + "user-cog": $fa-var-user-cog, + "arrow-up-1-9": $fa-var-arrow-up-1-9, + "sort-numeric-up": $fa-var-sort-numeric-up, + "door-closed": $fa-var-door-closed, + "shield-virus": $fa-var-shield-virus, + "dice-six": $fa-var-dice-six, + "mosquito-net": $fa-var-mosquito-net, + "bridge-water": $fa-var-bridge-water, + "person-booth": $fa-var-person-booth, + "text-width": $fa-var-text-width, + "hat-wizard": $fa-var-hat-wizard, + "pen-fancy": $fa-var-pen-fancy, + "person-digging": $fa-var-person-digging, + "digging": $fa-var-digging, + "trash": $fa-var-trash, + "gauge-simple": $fa-var-gauge-simple, + "gauge-simple-med": $fa-var-gauge-simple-med, + "tachometer-average": $fa-var-tachometer-average, + "book-medical": $fa-var-book-medical, + "poo": $fa-var-poo, + "quote-right": $fa-var-quote-right, + "quote-right-alt": $fa-var-quote-right-alt, + "shirt": $fa-var-shirt, + "t-shirt": $fa-var-t-shirt, + "tshirt": $fa-var-tshirt, + "cubes": $fa-var-cubes, + "divide": $fa-var-divide, + "tenge-sign": $fa-var-tenge-sign, + "tenge": $fa-var-tenge, + "headphones": $fa-var-headphones, + "hands-holding": $fa-var-hands-holding, + "hands-clapping": $fa-var-hands-clapping, + "republican": $fa-var-republican, + "arrow-left": $fa-var-arrow-left, + "person-circle-xmark": $fa-var-person-circle-xmark, + "ruler": $fa-var-ruler, + "align-left": $fa-var-align-left, + "dice-d6": $fa-var-dice-d6, + "restroom": $fa-var-restroom, + "j": $fa-var-j, + "users-viewfinder": $fa-var-users-viewfinder, + "file-video": $fa-var-file-video, + "up-right-from-square": $fa-var-up-right-from-square, + "external-link-alt": $fa-var-external-link-alt, + "table-cells": $fa-var-table-cells, + "th": $fa-var-th, + "file-pdf": $fa-var-file-pdf, + "book-bible": $fa-var-book-bible, + "bible": $fa-var-bible, + "o": $fa-var-o, + "suitcase-medical": $fa-var-suitcase-medical, + "medkit": $fa-var-medkit, + "user-secret": $fa-var-user-secret, + "otter": $fa-var-otter, + "person-dress": $fa-var-person-dress, + "female": $fa-var-female, + "comment-dollar": $fa-var-comment-dollar, + "business-time": $fa-var-business-time, + "briefcase-clock": $fa-var-briefcase-clock, + "table-cells-large": $fa-var-table-cells-large, + "th-large": $fa-var-th-large, + "book-tanakh": $fa-var-book-tanakh, + "tanakh": $fa-var-tanakh, + "phone-volume": $fa-var-phone-volume, + "volume-control-phone": $fa-var-volume-control-phone, + "hat-cowboy-side": $fa-var-hat-cowboy-side, + "clipboard-user": $fa-var-clipboard-user, + "child": $fa-var-child, + "lira-sign": $fa-var-lira-sign, + "satellite": $fa-var-satellite, + "plane-lock": $fa-var-plane-lock, + "tag": $fa-var-tag, + "comment": $fa-var-comment, + "cake-candles": $fa-var-cake-candles, + "birthday-cake": $fa-var-birthday-cake, + "cake": $fa-var-cake, + "envelope": $fa-var-envelope, + "angles-up": $fa-var-angles-up, + "angle-double-up": $fa-var-angle-double-up, + "paperclip": $fa-var-paperclip, + "arrow-right-to-city": $fa-var-arrow-right-to-city, + "ribbon": $fa-var-ribbon, + "lungs": $fa-var-lungs, + "arrow-up-9-1": $fa-var-arrow-up-9-1, + "sort-numeric-up-alt": $fa-var-sort-numeric-up-alt, + "litecoin-sign": $fa-var-litecoin-sign, + "border-none": $fa-var-border-none, + "circle-nodes": $fa-var-circle-nodes, + "parachute-box": $fa-var-parachute-box, + "indent": $fa-var-indent, + "truck-field-un": $fa-var-truck-field-un, + "hourglass": $fa-var-hourglass, + "hourglass-empty": $fa-var-hourglass-empty, + "mountain": $fa-var-mountain, + "user-doctor": $fa-var-user-doctor, + "user-md": $fa-var-user-md, + "circle-info": $fa-var-circle-info, + "info-circle": $fa-var-info-circle, + "cloud-meatball": $fa-var-cloud-meatball, + "camera": $fa-var-camera, + "camera-alt": $fa-var-camera-alt, + "square-virus": $fa-var-square-virus, + "meteor": $fa-var-meteor, + "car-on": $fa-var-car-on, + "sleigh": $fa-var-sleigh, + "arrow-down-1-9": $fa-var-arrow-down-1-9, + "sort-numeric-asc": $fa-var-sort-numeric-asc, + "sort-numeric-down": $fa-var-sort-numeric-down, + "hand-holding-droplet": $fa-var-hand-holding-droplet, + "hand-holding-water": $fa-var-hand-holding-water, + "water": $fa-var-water, + "calendar-check": $fa-var-calendar-check, + "braille": $fa-var-braille, + "prescription-bottle-medical": $fa-var-prescription-bottle-medical, + "prescription-bottle-alt": $fa-var-prescription-bottle-alt, + "landmark": $fa-var-landmark, + "truck": $fa-var-truck, + "crosshairs": $fa-var-crosshairs, + "person-cane": $fa-var-person-cane, + "tent": $fa-var-tent, + "vest-patches": $fa-var-vest-patches, + "check-double": $fa-var-check-double, + "arrow-down-a-z": $fa-var-arrow-down-a-z, + "sort-alpha-asc": $fa-var-sort-alpha-asc, + "sort-alpha-down": $fa-var-sort-alpha-down, + "money-bill-wheat": $fa-var-money-bill-wheat, + "cookie": $fa-var-cookie, + "arrow-rotate-left": $fa-var-arrow-rotate-left, + "arrow-left-rotate": $fa-var-arrow-left-rotate, + "arrow-rotate-back": $fa-var-arrow-rotate-back, + "arrow-rotate-backward": $fa-var-arrow-rotate-backward, + "undo": $fa-var-undo, + "hard-drive": $fa-var-hard-drive, + "hdd": $fa-var-hdd, + "face-grin-squint-tears": $fa-var-face-grin-squint-tears, + "grin-squint-tears": $fa-var-grin-squint-tears, + "dumbbell": $fa-var-dumbbell, + "rectangle-list": $fa-var-rectangle-list, + "list-alt": $fa-var-list-alt, + "tarp-droplet": $fa-var-tarp-droplet, + "house-medical-circle-check": $fa-var-house-medical-circle-check, + "person-skiing-nordic": $fa-var-person-skiing-nordic, + "skiing-nordic": $fa-var-skiing-nordic, + "calendar-plus": $fa-var-calendar-plus, + "plane-arrival": $fa-var-plane-arrival, + "circle-left": $fa-var-circle-left, + "arrow-alt-circle-left": $fa-var-arrow-alt-circle-left, + "train-subway": $fa-var-train-subway, + "subway": $fa-var-subway, + "chart-gantt": $fa-var-chart-gantt, + "indian-rupee-sign": $fa-var-indian-rupee-sign, + "indian-rupee": $fa-var-indian-rupee, + "inr": $fa-var-inr, + "crop-simple": $fa-var-crop-simple, + "crop-alt": $fa-var-crop-alt, + "money-bill-1": $fa-var-money-bill-1, + "money-bill-alt": $fa-var-money-bill-alt, + "left-long": $fa-var-left-long, + "long-arrow-alt-left": $fa-var-long-arrow-alt-left, + "dna": $fa-var-dna, + "virus-slash": $fa-var-virus-slash, + "minus": $fa-var-minus, + "subtract": $fa-var-subtract, + "chess": $fa-var-chess, + "arrow-left-long": $fa-var-arrow-left-long, + "long-arrow-left": $fa-var-long-arrow-left, + "plug-circle-check": $fa-var-plug-circle-check, + "street-view": $fa-var-street-view, + "franc-sign": $fa-var-franc-sign, + "volume-off": $fa-var-volume-off, + "hands-asl-interpreting": $fa-var-hands-asl-interpreting, + "american-sign-language-interpreting": $fa-var-american-sign-language-interpreting, + "asl-interpreting": $fa-var-asl-interpreting, + "hands-american-sign-language-interpreting": $fa-var-hands-american-sign-language-interpreting, + "gear": $fa-var-gear, + "cog": $fa-var-cog, + "droplet-slash": $fa-var-droplet-slash, + "tint-slash": $fa-var-tint-slash, + "mosque": $fa-var-mosque, + "mosquito": $fa-var-mosquito, + "star-of-david": $fa-var-star-of-david, + "person-military-rifle": $fa-var-person-military-rifle, + "cart-shopping": $fa-var-cart-shopping, + "shopping-cart": $fa-var-shopping-cart, + "vials": $fa-var-vials, + "plug-circle-plus": $fa-var-plug-circle-plus, + "place-of-worship": $fa-var-place-of-worship, + "grip-vertical": $fa-var-grip-vertical, + "arrow-turn-up": $fa-var-arrow-turn-up, + "level-up": $fa-var-level-up, + "u": $fa-var-u, + "square-root-variable": $fa-var-square-root-variable, + "square-root-alt": $fa-var-square-root-alt, + "clock": $fa-var-clock, + "clock-four": $fa-var-clock-four, + "backward-step": $fa-var-backward-step, + "step-backward": $fa-var-step-backward, + "pallet": $fa-var-pallet, + "faucet": $fa-var-faucet, + "baseball-bat-ball": $fa-var-baseball-bat-ball, + "s": $fa-var-s, + "timeline": $fa-var-timeline, + "keyboard": $fa-var-keyboard, + "caret-down": $fa-var-caret-down, + "house-chimney-medical": $fa-var-house-chimney-medical, + "clinic-medical": $fa-var-clinic-medical, + "temperature-three-quarters": $fa-var-temperature-three-quarters, + "temperature-3": $fa-var-temperature-3, + "thermometer-3": $fa-var-thermometer-3, + "thermometer-three-quarters": $fa-var-thermometer-three-quarters, + "mobile-screen": $fa-var-mobile-screen, + "mobile-android-alt": $fa-var-mobile-android-alt, + "plane-up": $fa-var-plane-up, + "piggy-bank": $fa-var-piggy-bank, + "battery-half": $fa-var-battery-half, + "battery-3": $fa-var-battery-3, + "mountain-city": $fa-var-mountain-city, + "coins": $fa-var-coins, + "khanda": $fa-var-khanda, + "sliders": $fa-var-sliders, + "sliders-h": $fa-var-sliders-h, + "folder-tree": $fa-var-folder-tree, + "network-wired": $fa-var-network-wired, + "map-pin": $fa-var-map-pin, + "hamsa": $fa-var-hamsa, + "cent-sign": $fa-var-cent-sign, + "flask": $fa-var-flask, + "person-pregnant": $fa-var-person-pregnant, + "wand-sparkles": $fa-var-wand-sparkles, + "ellipsis-vertical": $fa-var-ellipsis-vertical, + "ellipsis-v": $fa-var-ellipsis-v, + "ticket": $fa-var-ticket, + "power-off": $fa-var-power-off, + "right-long": $fa-var-right-long, + "long-arrow-alt-right": $fa-var-long-arrow-alt-right, + "flag-usa": $fa-var-flag-usa, + "laptop-file": $fa-var-laptop-file, + "tty": $fa-var-tty, + "teletype": $fa-var-teletype, + "diagram-next": $fa-var-diagram-next, + "person-rifle": $fa-var-person-rifle, + "house-medical-circle-exclamation": $fa-var-house-medical-circle-exclamation, + "closed-captioning": $fa-var-closed-captioning, + "person-hiking": $fa-var-person-hiking, + "hiking": $fa-var-hiking, + "venus-double": $fa-var-venus-double, + "images": $fa-var-images, + "calculator": $fa-var-calculator, + "people-pulling": $fa-var-people-pulling, + "n": $fa-var-n, + "cable-car": $fa-var-cable-car, + "tram": $fa-var-tram, + "cloud-rain": $fa-var-cloud-rain, + "building-circle-xmark": $fa-var-building-circle-xmark, + "ship": $fa-var-ship, + "arrows-down-to-line": $fa-var-arrows-down-to-line, + "download": $fa-var-download, + "face-grin": $fa-var-face-grin, + "grin": $fa-var-grin, + "delete-left": $fa-var-delete-left, + "backspace": $fa-var-backspace, + "eye-dropper": $fa-var-eye-dropper, + "eye-dropper-empty": $fa-var-eye-dropper-empty, + "eyedropper": $fa-var-eyedropper, + "file-circle-check": $fa-var-file-circle-check, + "forward": $fa-var-forward, + "mobile": $fa-var-mobile, + "mobile-android": $fa-var-mobile-android, + "mobile-phone": $fa-var-mobile-phone, + "face-meh": $fa-var-face-meh, + "meh": $fa-var-meh, + "align-center": $fa-var-align-center, + "book-skull": $fa-var-book-skull, + "book-dead": $fa-var-book-dead, + "id-card": $fa-var-id-card, + "drivers-license": $fa-var-drivers-license, + "outdent": $fa-var-outdent, + "dedent": $fa-var-dedent, + "heart-circle-exclamation": $fa-var-heart-circle-exclamation, + "house": $fa-var-house, + "home": $fa-var-home, + "home-alt": $fa-var-home-alt, + "home-lg-alt": $fa-var-home-lg-alt, + "calendar-week": $fa-var-calendar-week, + "laptop-medical": $fa-var-laptop-medical, + "b": $fa-var-b, + "file-medical": $fa-var-file-medical, + "dice-one": $fa-var-dice-one, + "kiwi-bird": $fa-var-kiwi-bird, + "arrow-right-arrow-left": $fa-var-arrow-right-arrow-left, + "exchange": $fa-var-exchange, + "rotate-right": $fa-var-rotate-right, + "redo-alt": $fa-var-redo-alt, + "rotate-forward": $fa-var-rotate-forward, + "utensils": $fa-var-utensils, + "cutlery": $fa-var-cutlery, + "arrow-up-wide-short": $fa-var-arrow-up-wide-short, + "sort-amount-up": $fa-var-sort-amount-up, + "mill-sign": $fa-var-mill-sign, + "bowl-rice": $fa-var-bowl-rice, + "skull": $fa-var-skull, + "tower-broadcast": $fa-var-tower-broadcast, + "broadcast-tower": $fa-var-broadcast-tower, + "truck-pickup": $fa-var-truck-pickup, + "up-long": $fa-var-up-long, + "long-arrow-alt-up": $fa-var-long-arrow-alt-up, + "stop": $fa-var-stop, + "code-merge": $fa-var-code-merge, + "upload": $fa-var-upload, + "hurricane": $fa-var-hurricane, + "mound": $fa-var-mound, + "toilet-portable": $fa-var-toilet-portable, + "compact-disc": $fa-var-compact-disc, + "file-arrow-down": $fa-var-file-arrow-down, + "file-download": $fa-var-file-download, + "caravan": $fa-var-caravan, + "shield-cat": $fa-var-shield-cat, + "bolt": $fa-var-bolt, + "zap": $fa-var-zap, + "glass-water": $fa-var-glass-water, + "oil-well": $fa-var-oil-well, + "vault": $fa-var-vault, + "mars": $fa-var-mars, + "toilet": $fa-var-toilet, + "plane-circle-xmark": $fa-var-plane-circle-xmark, + "yen-sign": $fa-var-yen-sign, + "cny": $fa-var-cny, + "jpy": $fa-var-jpy, + "rmb": $fa-var-rmb, + "yen": $fa-var-yen, + "ruble-sign": $fa-var-ruble-sign, + "rouble": $fa-var-rouble, + "rub": $fa-var-rub, + "ruble": $fa-var-ruble, + "sun": $fa-var-sun, + "guitar": $fa-var-guitar, + "face-laugh-wink": $fa-var-face-laugh-wink, + "laugh-wink": $fa-var-laugh-wink, + "horse-head": $fa-var-horse-head, + "bore-hole": $fa-var-bore-hole, + "industry": $fa-var-industry, + "circle-down": $fa-var-circle-down, + "arrow-alt-circle-down": $fa-var-arrow-alt-circle-down, + "arrows-turn-to-dots": $fa-var-arrows-turn-to-dots, + "florin-sign": $fa-var-florin-sign, + "arrow-down-short-wide": $fa-var-arrow-down-short-wide, + "sort-amount-desc": $fa-var-sort-amount-desc, + "sort-amount-down-alt": $fa-var-sort-amount-down-alt, + "less-than": $fa-var-less-than, + "angle-down": $fa-var-angle-down, + "car-tunnel": $fa-var-car-tunnel, + "head-side-cough": $fa-var-head-side-cough, + "grip-lines": $fa-var-grip-lines, + "thumbs-down": $fa-var-thumbs-down, + "user-lock": $fa-var-user-lock, + "arrow-right-long": $fa-var-arrow-right-long, + "long-arrow-right": $fa-var-long-arrow-right, + "anchor-circle-xmark": $fa-var-anchor-circle-xmark, + "ellipsis": $fa-var-ellipsis, + "ellipsis-h": $fa-var-ellipsis-h, + "chess-pawn": $fa-var-chess-pawn, + "kit-medical": $fa-var-kit-medical, + "first-aid": $fa-var-first-aid, + "person-through-window": $fa-var-person-through-window, + "toolbox": $fa-var-toolbox, + "hands-holding-circle": $fa-var-hands-holding-circle, + "bug": $fa-var-bug, + "credit-card": $fa-var-credit-card, + "credit-card-alt": $fa-var-credit-card-alt, + "car": $fa-var-car, + "automobile": $fa-var-automobile, + "hand-holding-hand": $fa-var-hand-holding-hand, + "book-open-reader": $fa-var-book-open-reader, + "book-reader": $fa-var-book-reader, + "mountain-sun": $fa-var-mountain-sun, + "arrows-left-right-to-line": $fa-var-arrows-left-right-to-line, + "dice-d20": $fa-var-dice-d20, + "truck-droplet": $fa-var-truck-droplet, + "file-circle-xmark": $fa-var-file-circle-xmark, + "temperature-arrow-up": $fa-var-temperature-arrow-up, + "temperature-up": $fa-var-temperature-up, + "medal": $fa-var-medal, + "bed": $fa-var-bed, + "square-h": $fa-var-square-h, + "h-square": $fa-var-h-square, + "podcast": $fa-var-podcast, + "temperature-full": $fa-var-temperature-full, + "temperature-4": $fa-var-temperature-4, + "thermometer-4": $fa-var-thermometer-4, + "thermometer-full": $fa-var-thermometer-full, + "bell": $fa-var-bell, + "superscript": $fa-var-superscript, + "plug-circle-xmark": $fa-var-plug-circle-xmark, + "star-of-life": $fa-var-star-of-life, + "phone-slash": $fa-var-phone-slash, + "paint-roller": $fa-var-paint-roller, + "handshake-angle": $fa-var-handshake-angle, + "hands-helping": $fa-var-hands-helping, + "location-dot": $fa-var-location-dot, + "map-marker-alt": $fa-var-map-marker-alt, + "file": $fa-var-file, + "greater-than": $fa-var-greater-than, + "person-swimming": $fa-var-person-swimming, + "swimmer": $fa-var-swimmer, + "arrow-down": $fa-var-arrow-down, + "droplet": $fa-var-droplet, + "tint": $fa-var-tint, + "eraser": $fa-var-eraser, + "earth-americas": $fa-var-earth-americas, + "earth": $fa-var-earth, + "earth-america": $fa-var-earth-america, + "globe-americas": $fa-var-globe-americas, + "person-burst": $fa-var-person-burst, + "dove": $fa-var-dove, + "battery-empty": $fa-var-battery-empty, + "battery-0": $fa-var-battery-0, + "socks": $fa-var-socks, + "inbox": $fa-var-inbox, + "section": $fa-var-section, + "gauge-high": $fa-var-gauge-high, + "tachometer-alt": $fa-var-tachometer-alt, + "tachometer-alt-fast": $fa-var-tachometer-alt-fast, + "envelope-open-text": $fa-var-envelope-open-text, + "hospital": $fa-var-hospital, + "hospital-alt": $fa-var-hospital-alt, + "hospital-wide": $fa-var-hospital-wide, + "wine-bottle": $fa-var-wine-bottle, + "chess-rook": $fa-var-chess-rook, + "bars-staggered": $fa-var-bars-staggered, + "reorder": $fa-var-reorder, + "stream": $fa-var-stream, + "dharmachakra": $fa-var-dharmachakra, + "hotdog": $fa-var-hotdog, + "person-walking-with-cane": $fa-var-person-walking-with-cane, + "blind": $fa-var-blind, + "drum": $fa-var-drum, + "ice-cream": $fa-var-ice-cream, + "heart-circle-bolt": $fa-var-heart-circle-bolt, + "fax": $fa-var-fax, + "paragraph": $fa-var-paragraph, + "check-to-slot": $fa-var-check-to-slot, + "vote-yea": $fa-var-vote-yea, + "star-half": $fa-var-star-half, + "boxes-stacked": $fa-var-boxes-stacked, + "boxes": $fa-var-boxes, + "boxes-alt": $fa-var-boxes-alt, + "link": $fa-var-link, + "chain": $fa-var-chain, + "ear-listen": $fa-var-ear-listen, + "assistive-listening-systems": $fa-var-assistive-listening-systems, + "tree-city": $fa-var-tree-city, + "play": $fa-var-play, + "font": $fa-var-font, + "rupiah-sign": $fa-var-rupiah-sign, + "magnifying-glass": $fa-var-magnifying-glass, + "search": $fa-var-search, + "table-tennis-paddle-ball": $fa-var-table-tennis-paddle-ball, + "ping-pong-paddle-ball": $fa-var-ping-pong-paddle-ball, + "table-tennis": $fa-var-table-tennis, + "person-dots-from-line": $fa-var-person-dots-from-line, + "diagnoses": $fa-var-diagnoses, + "trash-can-arrow-up": $fa-var-trash-can-arrow-up, + "trash-restore-alt": $fa-var-trash-restore-alt, + "naira-sign": $fa-var-naira-sign, + "cart-arrow-down": $fa-var-cart-arrow-down, + "walkie-talkie": $fa-var-walkie-talkie, + "file-pen": $fa-var-file-pen, + "file-edit": $fa-var-file-edit, + "receipt": $fa-var-receipt, + "square-pen": $fa-var-square-pen, + "pen-square": $fa-var-pen-square, + "pencil-square": $fa-var-pencil-square, + "suitcase-rolling": $fa-var-suitcase-rolling, + "person-circle-exclamation": $fa-var-person-circle-exclamation, + "chevron-down": $fa-var-chevron-down, + "battery-full": $fa-var-battery-full, + "battery": $fa-var-battery, + "battery-5": $fa-var-battery-5, + "skull-crossbones": $fa-var-skull-crossbones, + "code-compare": $fa-var-code-compare, + "list-ul": $fa-var-list-ul, + "list-dots": $fa-var-list-dots, + "school-lock": $fa-var-school-lock, + "tower-cell": $fa-var-tower-cell, + "down-long": $fa-var-down-long, + "long-arrow-alt-down": $fa-var-long-arrow-alt-down, + "ranking-star": $fa-var-ranking-star, + "chess-king": $fa-var-chess-king, + "person-harassing": $fa-var-person-harassing, + "brazilian-real-sign": $fa-var-brazilian-real-sign, + "landmark-dome": $fa-var-landmark-dome, + "landmark-alt": $fa-var-landmark-alt, + "arrow-up": $fa-var-arrow-up, + "tv": $fa-var-tv, + "television": $fa-var-television, + "tv-alt": $fa-var-tv-alt, + "shrimp": $fa-var-shrimp, + "list-check": $fa-var-list-check, + "tasks": $fa-var-tasks, + "jug-detergent": $fa-var-jug-detergent, + "circle-user": $fa-var-circle-user, + "user-circle": $fa-var-user-circle, + "user-shield": $fa-var-user-shield, + "wind": $fa-var-wind, + "car-burst": $fa-var-car-burst, + "car-crash": $fa-var-car-crash, + "y": $fa-var-y, + "person-snowboarding": $fa-var-person-snowboarding, + "snowboarding": $fa-var-snowboarding, + "truck-fast": $fa-var-truck-fast, + "shipping-fast": $fa-var-shipping-fast, + "fish": $fa-var-fish, + "user-graduate": $fa-var-user-graduate, + "circle-half-stroke": $fa-var-circle-half-stroke, + "adjust": $fa-var-adjust, + "clapperboard": $fa-var-clapperboard, + "circle-radiation": $fa-var-circle-radiation, + "radiation-alt": $fa-var-radiation-alt, + "baseball": $fa-var-baseball, + "baseball-ball": $fa-var-baseball-ball, + "jet-fighter-up": $fa-var-jet-fighter-up, + "diagram-project": $fa-var-diagram-project, + "project-diagram": $fa-var-project-diagram, + "copy": $fa-var-copy, + "volume-xmark": $fa-var-volume-xmark, + "volume-mute": $fa-var-volume-mute, + "volume-times": $fa-var-volume-times, + "hand-sparkles": $fa-var-hand-sparkles, + "grip": $fa-var-grip, + "grip-horizontal": $fa-var-grip-horizontal, + "share-from-square": $fa-var-share-from-square, + "share-square": $fa-var-share-square, + "child-combatant": $fa-var-child-combatant, + "child-rifle": $fa-var-child-rifle, + "gun": $fa-var-gun, + "square-phone": $fa-var-square-phone, + "phone-square": $fa-var-phone-square, + "plus": $fa-var-plus, + "add": $fa-var-add, + "expand": $fa-var-expand, + "computer": $fa-var-computer, + "xmark": $fa-var-xmark, + "close": $fa-var-close, + "multiply": $fa-var-multiply, + "remove": $fa-var-remove, + "times": $fa-var-times, + "arrows-up-down-left-right": $fa-var-arrows-up-down-left-right, + "arrows": $fa-var-arrows, + "chalkboard-user": $fa-var-chalkboard-user, + "chalkboard-teacher": $fa-var-chalkboard-teacher, + "peso-sign": $fa-var-peso-sign, + "building-shield": $fa-var-building-shield, + "baby": $fa-var-baby, + "users-line": $fa-var-users-line, + "quote-left": $fa-var-quote-left, + "quote-left-alt": $fa-var-quote-left-alt, + "tractor": $fa-var-tractor, + "trash-arrow-up": $fa-var-trash-arrow-up, + "trash-restore": $fa-var-trash-restore, + "arrow-down-up-lock": $fa-var-arrow-down-up-lock, + "lines-leaning": $fa-var-lines-leaning, + "ruler-combined": $fa-var-ruler-combined, + "copyright": $fa-var-copyright, + "equals": $fa-var-equals, + "blender": $fa-var-blender, + "teeth": $fa-var-teeth, + "shekel-sign": $fa-var-shekel-sign, + "ils": $fa-var-ils, + "shekel": $fa-var-shekel, + "sheqel": $fa-var-sheqel, + "sheqel-sign": $fa-var-sheqel-sign, + "map": $fa-var-map, + "rocket": $fa-var-rocket, + "photo-film": $fa-var-photo-film, + "photo-video": $fa-var-photo-video, + "folder-minus": $fa-var-folder-minus, + "store": $fa-var-store, + "arrow-trend-up": $fa-var-arrow-trend-up, + "plug-circle-minus": $fa-var-plug-circle-minus, + "sign-hanging": $fa-var-sign-hanging, + "sign": $fa-var-sign, + "bezier-curve": $fa-var-bezier-curve, + "bell-slash": $fa-var-bell-slash, + "tablet": $fa-var-tablet, + "tablet-android": $fa-var-tablet-android, + "school-flag": $fa-var-school-flag, + "fill": $fa-var-fill, + "angle-up": $fa-var-angle-up, + "drumstick-bite": $fa-var-drumstick-bite, + "holly-berry": $fa-var-holly-berry, + "chevron-left": $fa-var-chevron-left, + "bacteria": $fa-var-bacteria, + "hand-lizard": $fa-var-hand-lizard, + "notdef": $fa-var-notdef, + "disease": $fa-var-disease, + "briefcase-medical": $fa-var-briefcase-medical, + "genderless": $fa-var-genderless, + "chevron-right": $fa-var-chevron-right, + "retweet": $fa-var-retweet, + "car-rear": $fa-var-car-rear, + "car-alt": $fa-var-car-alt, + "pump-soap": $fa-var-pump-soap, + "video-slash": $fa-var-video-slash, + "battery-quarter": $fa-var-battery-quarter, + "battery-2": $fa-var-battery-2, + "radio": $fa-var-radio, + "baby-carriage": $fa-var-baby-carriage, + "carriage-baby": $fa-var-carriage-baby, + "traffic-light": $fa-var-traffic-light, + "thermometer": $fa-var-thermometer, + "vr-cardboard": $fa-var-vr-cardboard, + "hand-middle-finger": $fa-var-hand-middle-finger, + "percent": $fa-var-percent, + "percentage": $fa-var-percentage, + "truck-moving": $fa-var-truck-moving, + "glass-water-droplet": $fa-var-glass-water-droplet, + "display": $fa-var-display, + "face-smile": $fa-var-face-smile, + "smile": $fa-var-smile, + "thumbtack": $fa-var-thumbtack, + "thumb-tack": $fa-var-thumb-tack, + "trophy": $fa-var-trophy, + "person-praying": $fa-var-person-praying, + "pray": $fa-var-pray, + "hammer": $fa-var-hammer, + "hand-peace": $fa-var-hand-peace, + "rotate": $fa-var-rotate, + "sync-alt": $fa-var-sync-alt, + "spinner": $fa-var-spinner, + "robot": $fa-var-robot, + "peace": $fa-var-peace, + "gears": $fa-var-gears, + "cogs": $fa-var-cogs, + "warehouse": $fa-var-warehouse, + "arrow-up-right-dots": $fa-var-arrow-up-right-dots, + "splotch": $fa-var-splotch, + "face-grin-hearts": $fa-var-face-grin-hearts, + "grin-hearts": $fa-var-grin-hearts, + "dice-four": $fa-var-dice-four, + "sim-card": $fa-var-sim-card, + "transgender": $fa-var-transgender, + "transgender-alt": $fa-var-transgender-alt, + "mercury": $fa-var-mercury, + "arrow-turn-down": $fa-var-arrow-turn-down, + "level-down": $fa-var-level-down, + "person-falling-burst": $fa-var-person-falling-burst, + "award": $fa-var-award, + "ticket-simple": $fa-var-ticket-simple, + "ticket-alt": $fa-var-ticket-alt, + "building": $fa-var-building, + "angles-left": $fa-var-angles-left, + "angle-double-left": $fa-var-angle-double-left, + "qrcode": $fa-var-qrcode, + "clock-rotate-left": $fa-var-clock-rotate-left, + "history": $fa-var-history, + "face-grin-beam-sweat": $fa-var-face-grin-beam-sweat, + "grin-beam-sweat": $fa-var-grin-beam-sweat, + "file-export": $fa-var-file-export, + "arrow-right-from-file": $fa-var-arrow-right-from-file, + "shield": $fa-var-shield, + "shield-blank": $fa-var-shield-blank, + "arrow-up-short-wide": $fa-var-arrow-up-short-wide, + "sort-amount-up-alt": $fa-var-sort-amount-up-alt, + "house-medical": $fa-var-house-medical, + "golf-ball-tee": $fa-var-golf-ball-tee, + "golf-ball": $fa-var-golf-ball, + "circle-chevron-left": $fa-var-circle-chevron-left, + "chevron-circle-left": $fa-var-chevron-circle-left, + "house-chimney-window": $fa-var-house-chimney-window, + "pen-nib": $fa-var-pen-nib, + "tent-arrow-turn-left": $fa-var-tent-arrow-turn-left, + "tents": $fa-var-tents, + "wand-magic": $fa-var-wand-magic, + "magic": $fa-var-magic, + "dog": $fa-var-dog, + "carrot": $fa-var-carrot, + "moon": $fa-var-moon, + "wine-glass-empty": $fa-var-wine-glass-empty, + "wine-glass-alt": $fa-var-wine-glass-alt, + "cheese": $fa-var-cheese, + "yin-yang": $fa-var-yin-yang, + "music": $fa-var-music, + "code-commit": $fa-var-code-commit, + "temperature-low": $fa-var-temperature-low, + "person-biking": $fa-var-person-biking, + "biking": $fa-var-biking, + "broom": $fa-var-broom, + "shield-heart": $fa-var-shield-heart, + "gopuram": $fa-var-gopuram, + "earth-oceania": $fa-var-earth-oceania, + "globe-oceania": $fa-var-globe-oceania, + "square-xmark": $fa-var-square-xmark, + "times-square": $fa-var-times-square, + "xmark-square": $fa-var-xmark-square, + "hashtag": $fa-var-hashtag, + "up-right-and-down-left-from-center": $fa-var-up-right-and-down-left-from-center, + "expand-alt": $fa-var-expand-alt, + "oil-can": $fa-var-oil-can, + "t": $fa-var-t, + "hippo": $fa-var-hippo, + "chart-column": $fa-var-chart-column, + "infinity": $fa-var-infinity, + "vial-circle-check": $fa-var-vial-circle-check, + "person-arrow-down-to-line": $fa-var-person-arrow-down-to-line, + "voicemail": $fa-var-voicemail, + "fan": $fa-var-fan, + "person-walking-luggage": $fa-var-person-walking-luggage, + "up-down": $fa-var-up-down, + "arrows-alt-v": $fa-var-arrows-alt-v, + "cloud-moon-rain": $fa-var-cloud-moon-rain, + "calendar": $fa-var-calendar, + "trailer": $fa-var-trailer, + "bahai": $fa-var-bahai, + "haykal": $fa-var-haykal, + "sd-card": $fa-var-sd-card, + "dragon": $fa-var-dragon, + "shoe-prints": $fa-var-shoe-prints, + "circle-plus": $fa-var-circle-plus, + "plus-circle": $fa-var-plus-circle, + "face-grin-tongue-wink": $fa-var-face-grin-tongue-wink, + "grin-tongue-wink": $fa-var-grin-tongue-wink, + "hand-holding": $fa-var-hand-holding, + "plug-circle-exclamation": $fa-var-plug-circle-exclamation, + "link-slash": $fa-var-link-slash, + "chain-broken": $fa-var-chain-broken, + "chain-slash": $fa-var-chain-slash, + "unlink": $fa-var-unlink, + "clone": $fa-var-clone, + "person-walking-arrow-loop-left": $fa-var-person-walking-arrow-loop-left, + "arrow-up-z-a": $fa-var-arrow-up-z-a, + "sort-alpha-up-alt": $fa-var-sort-alpha-up-alt, + "fire-flame-curved": $fa-var-fire-flame-curved, + "fire-alt": $fa-var-fire-alt, + "tornado": $fa-var-tornado, + "file-circle-plus": $fa-var-file-circle-plus, + "book-quran": $fa-var-book-quran, + "quran": $fa-var-quran, + "anchor": $fa-var-anchor, + "border-all": $fa-var-border-all, + "face-angry": $fa-var-face-angry, + "angry": $fa-var-angry, + "cookie-bite": $fa-var-cookie-bite, + "arrow-trend-down": $fa-var-arrow-trend-down, + "rss": $fa-var-rss, + "feed": $fa-var-feed, + "draw-polygon": $fa-var-draw-polygon, + "scale-balanced": $fa-var-scale-balanced, + "balance-scale": $fa-var-balance-scale, + "gauge-simple-high": $fa-var-gauge-simple-high, + "tachometer": $fa-var-tachometer, + "tachometer-fast": $fa-var-tachometer-fast, + "shower": $fa-var-shower, + "desktop": $fa-var-desktop, + "desktop-alt": $fa-var-desktop-alt, + "m": $fa-var-m, + "table-list": $fa-var-table-list, + "th-list": $fa-var-th-list, + "comment-sms": $fa-var-comment-sms, + "sms": $fa-var-sms, + "book": $fa-var-book, + "user-plus": $fa-var-user-plus, + "check": $fa-var-check, + "battery-three-quarters": $fa-var-battery-three-quarters, + "battery-4": $fa-var-battery-4, + "house-circle-check": $fa-var-house-circle-check, + "angle-left": $fa-var-angle-left, + "diagram-successor": $fa-var-diagram-successor, + "truck-arrow-right": $fa-var-truck-arrow-right, + "arrows-split-up-and-left": $fa-var-arrows-split-up-and-left, + "hand-fist": $fa-var-hand-fist, + "fist-raised": $fa-var-fist-raised, + "cloud-moon": $fa-var-cloud-moon, + "briefcase": $fa-var-briefcase, + "person-falling": $fa-var-person-falling, + "image-portrait": $fa-var-image-portrait, + "portrait": $fa-var-portrait, + "user-tag": $fa-var-user-tag, + "rug": $fa-var-rug, + "earth-europe": $fa-var-earth-europe, + "globe-europe": $fa-var-globe-europe, + "cart-flatbed-suitcase": $fa-var-cart-flatbed-suitcase, + "luggage-cart": $fa-var-luggage-cart, + "rectangle-xmark": $fa-var-rectangle-xmark, + "rectangle-times": $fa-var-rectangle-times, + "times-rectangle": $fa-var-times-rectangle, + "window-close": $fa-var-window-close, + "baht-sign": $fa-var-baht-sign, + "book-open": $fa-var-book-open, + "book-journal-whills": $fa-var-book-journal-whills, + "journal-whills": $fa-var-journal-whills, + "handcuffs": $fa-var-handcuffs, + "triangle-exclamation": $fa-var-triangle-exclamation, + "exclamation-triangle": $fa-var-exclamation-triangle, + "warning": $fa-var-warning, + "database": $fa-var-database, + "share": $fa-var-share, + "arrow-turn-right": $fa-var-arrow-turn-right, + "mail-forward": $fa-var-mail-forward, + "bottle-droplet": $fa-var-bottle-droplet, + "mask-face": $fa-var-mask-face, + "hill-rockslide": $fa-var-hill-rockslide, + "right-left": $fa-var-right-left, + "exchange-alt": $fa-var-exchange-alt, + "paper-plane": $fa-var-paper-plane, + "road-circle-exclamation": $fa-var-road-circle-exclamation, + "dungeon": $fa-var-dungeon, + "align-right": $fa-var-align-right, + "money-bill-1-wave": $fa-var-money-bill-1-wave, + "money-bill-wave-alt": $fa-var-money-bill-wave-alt, + "life-ring": $fa-var-life-ring, + "hands": $fa-var-hands, + "sign-language": $fa-var-sign-language, + "signing": $fa-var-signing, + "calendar-day": $fa-var-calendar-day, + "water-ladder": $fa-var-water-ladder, + "ladder-water": $fa-var-ladder-water, + "swimming-pool": $fa-var-swimming-pool, + "arrows-up-down": $fa-var-arrows-up-down, + "arrows-v": $fa-var-arrows-v, + "face-grimace": $fa-var-face-grimace, + "grimace": $fa-var-grimace, + "wheelchair-move": $fa-var-wheelchair-move, + "wheelchair-alt": $fa-var-wheelchair-alt, + "turn-down": $fa-var-turn-down, + "level-down-alt": $fa-var-level-down-alt, + "person-walking-arrow-right": $fa-var-person-walking-arrow-right, + "square-envelope": $fa-var-square-envelope, + "envelope-square": $fa-var-envelope-square, + "dice": $fa-var-dice, + "bowling-ball": $fa-var-bowling-ball, + "brain": $fa-var-brain, + "bandage": $fa-var-bandage, + "band-aid": $fa-var-band-aid, + "calendar-minus": $fa-var-calendar-minus, + "circle-xmark": $fa-var-circle-xmark, + "times-circle": $fa-var-times-circle, + "xmark-circle": $fa-var-xmark-circle, + "gifts": $fa-var-gifts, + "hotel": $fa-var-hotel, + "earth-asia": $fa-var-earth-asia, + "globe-asia": $fa-var-globe-asia, + "id-card-clip": $fa-var-id-card-clip, + "id-card-alt": $fa-var-id-card-alt, + "magnifying-glass-plus": $fa-var-magnifying-glass-plus, + "search-plus": $fa-var-search-plus, + "thumbs-up": $fa-var-thumbs-up, + "user-clock": $fa-var-user-clock, + "hand-dots": $fa-var-hand-dots, + "allergies": $fa-var-allergies, + "file-invoice": $fa-var-file-invoice, + "window-minimize": $fa-var-window-minimize, + "mug-saucer": $fa-var-mug-saucer, + "coffee": $fa-var-coffee, + "brush": $fa-var-brush, + "mask": $fa-var-mask, + "magnifying-glass-minus": $fa-var-magnifying-glass-minus, + "search-minus": $fa-var-search-minus, + "ruler-vertical": $fa-var-ruler-vertical, + "user-large": $fa-var-user-large, + "user-alt": $fa-var-user-alt, + "train-tram": $fa-var-train-tram, + "user-nurse": $fa-var-user-nurse, + "syringe": $fa-var-syringe, + "cloud-sun": $fa-var-cloud-sun, + "stopwatch-20": $fa-var-stopwatch-20, + "square-full": $fa-var-square-full, + "magnet": $fa-var-magnet, + "jar": $fa-var-jar, + "note-sticky": $fa-var-note-sticky, + "sticky-note": $fa-var-sticky-note, + "bug-slash": $fa-var-bug-slash, + "arrow-up-from-water-pump": $fa-var-arrow-up-from-water-pump, + "bone": $fa-var-bone, + "user-injured": $fa-var-user-injured, + "face-sad-tear": $fa-var-face-sad-tear, + "sad-tear": $fa-var-sad-tear, + "plane": $fa-var-plane, + "tent-arrows-down": $fa-var-tent-arrows-down, + "exclamation": $fa-var-exclamation, + "arrows-spin": $fa-var-arrows-spin, + "print": $fa-var-print, + "turkish-lira-sign": $fa-var-turkish-lira-sign, + "try": $fa-var-try, + "turkish-lira": $fa-var-turkish-lira, + "dollar-sign": $fa-var-dollar-sign, + "dollar": $fa-var-dollar, + "usd": $fa-var-usd, + "x": $fa-var-x, + "magnifying-glass-dollar": $fa-var-magnifying-glass-dollar, + "search-dollar": $fa-var-search-dollar, + "users-gear": $fa-var-users-gear, + "users-cog": $fa-var-users-cog, + "person-military-pointing": $fa-var-person-military-pointing, + "building-columns": $fa-var-building-columns, + "bank": $fa-var-bank, + "institution": $fa-var-institution, + "museum": $fa-var-museum, + "university": $fa-var-university, + "umbrella": $fa-var-umbrella, + "trowel": $fa-var-trowel, + "d": $fa-var-d, + "stapler": $fa-var-stapler, + "masks-theater": $fa-var-masks-theater, + "theater-masks": $fa-var-theater-masks, + "kip-sign": $fa-var-kip-sign, + "hand-point-left": $fa-var-hand-point-left, + "handshake-simple": $fa-var-handshake-simple, + "handshake-alt": $fa-var-handshake-alt, + "jet-fighter": $fa-var-jet-fighter, + "fighter-jet": $fa-var-fighter-jet, + "square-share-nodes": $fa-var-square-share-nodes, + "share-alt-square": $fa-var-share-alt-square, + "barcode": $fa-var-barcode, + "plus-minus": $fa-var-plus-minus, + "video": $fa-var-video, + "video-camera": $fa-var-video-camera, + "graduation-cap": $fa-var-graduation-cap, + "mortar-board": $fa-var-mortar-board, + "hand-holding-medical": $fa-var-hand-holding-medical, + "person-circle-check": $fa-var-person-circle-check, + "turn-up": $fa-var-turn-up, + "level-up-alt": $fa-var-level-up-alt, +); + +$fa-brand-icons: ( + "monero": $fa-var-monero, + "hooli": $fa-var-hooli, + "yelp": $fa-var-yelp, + "cc-visa": $fa-var-cc-visa, + "lastfm": $fa-var-lastfm, + "shopware": $fa-var-shopware, + "creative-commons-nc": $fa-var-creative-commons-nc, + "aws": $fa-var-aws, + "redhat": $fa-var-redhat, + "yoast": $fa-var-yoast, + "cloudflare": $fa-var-cloudflare, + "ups": $fa-var-ups, + "wpexplorer": $fa-var-wpexplorer, + "dyalog": $fa-var-dyalog, + "bity": $fa-var-bity, + "stackpath": $fa-var-stackpath, + "buysellads": $fa-var-buysellads, + "first-order": $fa-var-first-order, + "modx": $fa-var-modx, + "guilded": $fa-var-guilded, + "vnv": $fa-var-vnv, + "square-js": $fa-var-square-js, + "js-square": $fa-var-js-square, + "microsoft": $fa-var-microsoft, + "qq": $fa-var-qq, + "orcid": $fa-var-orcid, + "java": $fa-var-java, + "invision": $fa-var-invision, + "creative-commons-pd-alt": $fa-var-creative-commons-pd-alt, + "centercode": $fa-var-centercode, + "glide-g": $fa-var-glide-g, + "drupal": $fa-var-drupal, + "hire-a-helper": $fa-var-hire-a-helper, + "creative-commons-by": $fa-var-creative-commons-by, + "unity": $fa-var-unity, + "whmcs": $fa-var-whmcs, + "rocketchat": $fa-var-rocketchat, + "vk": $fa-var-vk, + "untappd": $fa-var-untappd, + "mailchimp": $fa-var-mailchimp, + "css3-alt": $fa-var-css3-alt, + "square-reddit": $fa-var-square-reddit, + "reddit-square": $fa-var-reddit-square, + "vimeo-v": $fa-var-vimeo-v, + "contao": $fa-var-contao, + "square-font-awesome": $fa-var-square-font-awesome, + "deskpro": $fa-var-deskpro, + "sistrix": $fa-var-sistrix, + "square-instagram": $fa-var-square-instagram, + "instagram-square": $fa-var-instagram-square, + "battle-net": $fa-var-battle-net, + "the-red-yeti": $fa-var-the-red-yeti, + "square-hacker-news": $fa-var-square-hacker-news, + "hacker-news-square": $fa-var-hacker-news-square, + "edge": $fa-var-edge, + "napster": $fa-var-napster, + "square-snapchat": $fa-var-square-snapchat, + "snapchat-square": $fa-var-snapchat-square, + "google-plus-g": $fa-var-google-plus-g, + "artstation": $fa-var-artstation, + "markdown": $fa-var-markdown, + "sourcetree": $fa-var-sourcetree, + "google-plus": $fa-var-google-plus, + "diaspora": $fa-var-diaspora, + "foursquare": $fa-var-foursquare, + "stack-overflow": $fa-var-stack-overflow, + "github-alt": $fa-var-github-alt, + "phoenix-squadron": $fa-var-phoenix-squadron, + "pagelines": $fa-var-pagelines, + "algolia": $fa-var-algolia, + "red-river": $fa-var-red-river, + "creative-commons-sa": $fa-var-creative-commons-sa, + "safari": $fa-var-safari, + "google": $fa-var-google, + "square-font-awesome-stroke": $fa-var-square-font-awesome-stroke, + "font-awesome-alt": $fa-var-font-awesome-alt, + "atlassian": $fa-var-atlassian, + "linkedin-in": $fa-var-linkedin-in, + "digital-ocean": $fa-var-digital-ocean, + "nimblr": $fa-var-nimblr, + "chromecast": $fa-var-chromecast, + "evernote": $fa-var-evernote, + "hacker-news": $fa-var-hacker-news, + "creative-commons-sampling": $fa-var-creative-commons-sampling, + "adversal": $fa-var-adversal, + "creative-commons": $fa-var-creative-commons, + "watchman-monitoring": $fa-var-watchman-monitoring, + "fonticons": $fa-var-fonticons, + "weixin": $fa-var-weixin, + "shirtsinbulk": $fa-var-shirtsinbulk, + "codepen": $fa-var-codepen, + "git-alt": $fa-var-git-alt, + "lyft": $fa-var-lyft, + "rev": $fa-var-rev, + "windows": $fa-var-windows, + "wizards-of-the-coast": $fa-var-wizards-of-the-coast, + "square-viadeo": $fa-var-square-viadeo, + "viadeo-square": $fa-var-viadeo-square, + "meetup": $fa-var-meetup, + "centos": $fa-var-centos, + "adn": $fa-var-adn, + "cloudsmith": $fa-var-cloudsmith, + "pied-piper-alt": $fa-var-pied-piper-alt, + "square-dribbble": $fa-var-square-dribbble, + "dribbble-square": $fa-var-dribbble-square, + "codiepie": $fa-var-codiepie, + "node": $fa-var-node, + "mix": $fa-var-mix, + "steam": $fa-var-steam, + "cc-apple-pay": $fa-var-cc-apple-pay, + "scribd": $fa-var-scribd, + "openid": $fa-var-openid, + "instalod": $fa-var-instalod, + "expeditedssl": $fa-var-expeditedssl, + "sellcast": $fa-var-sellcast, + "square-twitter": $fa-var-square-twitter, + "twitter-square": $fa-var-twitter-square, + "r-project": $fa-var-r-project, + "delicious": $fa-var-delicious, + "freebsd": $fa-var-freebsd, + "vuejs": $fa-var-vuejs, + "accusoft": $fa-var-accusoft, + "ioxhost": $fa-var-ioxhost, + "fonticons-fi": $fa-var-fonticons-fi, + "app-store": $fa-var-app-store, + "cc-mastercard": $fa-var-cc-mastercard, + "itunes-note": $fa-var-itunes-note, + "golang": $fa-var-golang, + "kickstarter": $fa-var-kickstarter, + "grav": $fa-var-grav, + "weibo": $fa-var-weibo, + "uncharted": $fa-var-uncharted, + "firstdraft": $fa-var-firstdraft, + "square-youtube": $fa-var-square-youtube, + "youtube-square": $fa-var-youtube-square, + "wikipedia-w": $fa-var-wikipedia-w, + "wpressr": $fa-var-wpressr, + "rendact": $fa-var-rendact, + "angellist": $fa-var-angellist, + "galactic-republic": $fa-var-galactic-republic, + "nfc-directional": $fa-var-nfc-directional, + "skype": $fa-var-skype, + "joget": $fa-var-joget, + "fedora": $fa-var-fedora, + "stripe-s": $fa-var-stripe-s, + "meta": $fa-var-meta, + "laravel": $fa-var-laravel, + "hotjar": $fa-var-hotjar, + "bluetooth-b": $fa-var-bluetooth-b, + "sticker-mule": $fa-var-sticker-mule, + "creative-commons-zero": $fa-var-creative-commons-zero, + "hips": $fa-var-hips, + "behance": $fa-var-behance, + "reddit": $fa-var-reddit, + "discord": $fa-var-discord, + "chrome": $fa-var-chrome, + "app-store-ios": $fa-var-app-store-ios, + "cc-discover": $fa-var-cc-discover, + "wpbeginner": $fa-var-wpbeginner, + "confluence": $fa-var-confluence, + "mdb": $fa-var-mdb, + "dochub": $fa-var-dochub, + "accessible-icon": $fa-var-accessible-icon, + "ebay": $fa-var-ebay, + "amazon": $fa-var-amazon, + "unsplash": $fa-var-unsplash, + "yarn": $fa-var-yarn, + "square-steam": $fa-var-square-steam, + "steam-square": $fa-var-steam-square, + "500px": $fa-var-500px, + "square-vimeo": $fa-var-square-vimeo, + "vimeo-square": $fa-var-vimeo-square, + "asymmetrik": $fa-var-asymmetrik, + "font-awesome": $fa-var-font-awesome, + "font-awesome-flag": $fa-var-font-awesome-flag, + "font-awesome-logo-full": $fa-var-font-awesome-logo-full, + "gratipay": $fa-var-gratipay, + "apple": $fa-var-apple, + "hive": $fa-var-hive, + "gitkraken": $fa-var-gitkraken, + "keybase": $fa-var-keybase, + "apple-pay": $fa-var-apple-pay, + "padlet": $fa-var-padlet, + "amazon-pay": $fa-var-amazon-pay, + "square-github": $fa-var-square-github, + "github-square": $fa-var-github-square, + "stumbleupon": $fa-var-stumbleupon, + "fedex": $fa-var-fedex, + "phoenix-framework": $fa-var-phoenix-framework, + "shopify": $fa-var-shopify, + "neos": $fa-var-neos, + "hackerrank": $fa-var-hackerrank, + "researchgate": $fa-var-researchgate, + "swift": $fa-var-swift, + "angular": $fa-var-angular, + "speakap": $fa-var-speakap, + "angrycreative": $fa-var-angrycreative, + "y-combinator": $fa-var-y-combinator, + "empire": $fa-var-empire, + "envira": $fa-var-envira, + "square-gitlab": $fa-var-square-gitlab, + "gitlab-square": $fa-var-gitlab-square, + "studiovinari": $fa-var-studiovinari, + "pied-piper": $fa-var-pied-piper, + "wordpress": $fa-var-wordpress, + "product-hunt": $fa-var-product-hunt, + "firefox": $fa-var-firefox, + "linode": $fa-var-linode, + "goodreads": $fa-var-goodreads, + "square-odnoklassniki": $fa-var-square-odnoklassniki, + "odnoklassniki-square": $fa-var-odnoklassniki-square, + "jsfiddle": $fa-var-jsfiddle, + "sith": $fa-var-sith, + "themeisle": $fa-var-themeisle, + "page4": $fa-var-page4, + "hashnode": $fa-var-hashnode, + "react": $fa-var-react, + "cc-paypal": $fa-var-cc-paypal, + "squarespace": $fa-var-squarespace, + "cc-stripe": $fa-var-cc-stripe, + "creative-commons-share": $fa-var-creative-commons-share, + "bitcoin": $fa-var-bitcoin, + "keycdn": $fa-var-keycdn, + "opera": $fa-var-opera, + "itch-io": $fa-var-itch-io, + "umbraco": $fa-var-umbraco, + "galactic-senate": $fa-var-galactic-senate, + "ubuntu": $fa-var-ubuntu, + "draft2digital": $fa-var-draft2digital, + "stripe": $fa-var-stripe, + "houzz": $fa-var-houzz, + "gg": $fa-var-gg, + "dhl": $fa-var-dhl, + "square-pinterest": $fa-var-square-pinterest, + "pinterest-square": $fa-var-pinterest-square, + "xing": $fa-var-xing, + "blackberry": $fa-var-blackberry, + "creative-commons-pd": $fa-var-creative-commons-pd, + "playstation": $fa-var-playstation, + "quinscape": $fa-var-quinscape, + "less": $fa-var-less, + "blogger-b": $fa-var-blogger-b, + "opencart": $fa-var-opencart, + "vine": $fa-var-vine, + "paypal": $fa-var-paypal, + "gitlab": $fa-var-gitlab, + "typo3": $fa-var-typo3, + "reddit-alien": $fa-var-reddit-alien, + "yahoo": $fa-var-yahoo, + "dailymotion": $fa-var-dailymotion, + "affiliatetheme": $fa-var-affiliatetheme, + "pied-piper-pp": $fa-var-pied-piper-pp, + "bootstrap": $fa-var-bootstrap, + "odnoklassniki": $fa-var-odnoklassniki, + "nfc-symbol": $fa-var-nfc-symbol, + "ethereum": $fa-var-ethereum, + "speaker-deck": $fa-var-speaker-deck, + "creative-commons-nc-eu": $fa-var-creative-commons-nc-eu, + "patreon": $fa-var-patreon, + "avianex": $fa-var-avianex, + "ello": $fa-var-ello, + "gofore": $fa-var-gofore, + "bimobject": $fa-var-bimobject, + "facebook-f": $fa-var-facebook-f, + "square-google-plus": $fa-var-square-google-plus, + "google-plus-square": $fa-var-google-plus-square, + "mandalorian": $fa-var-mandalorian, + "first-order-alt": $fa-var-first-order-alt, + "osi": $fa-var-osi, + "google-wallet": $fa-var-google-wallet, + "d-and-d-beyond": $fa-var-d-and-d-beyond, + "periscope": $fa-var-periscope, + "fulcrum": $fa-var-fulcrum, + "cloudscale": $fa-var-cloudscale, + "forumbee": $fa-var-forumbee, + "mizuni": $fa-var-mizuni, + "schlix": $fa-var-schlix, + "square-xing": $fa-var-square-xing, + "xing-square": $fa-var-xing-square, + "bandcamp": $fa-var-bandcamp, + "wpforms": $fa-var-wpforms, + "cloudversify": $fa-var-cloudversify, + "usps": $fa-var-usps, + "megaport": $fa-var-megaport, + "magento": $fa-var-magento, + "spotify": $fa-var-spotify, + "optin-monster": $fa-var-optin-monster, + "fly": $fa-var-fly, + "aviato": $fa-var-aviato, + "itunes": $fa-var-itunes, + "cuttlefish": $fa-var-cuttlefish, + "blogger": $fa-var-blogger, + "flickr": $fa-var-flickr, + "viber": $fa-var-viber, + "soundcloud": $fa-var-soundcloud, + "digg": $fa-var-digg, + "tencent-weibo": $fa-var-tencent-weibo, + "symfony": $fa-var-symfony, + "maxcdn": $fa-var-maxcdn, + "etsy": $fa-var-etsy, + "facebook-messenger": $fa-var-facebook-messenger, + "audible": $fa-var-audible, + "think-peaks": $fa-var-think-peaks, + "bilibili": $fa-var-bilibili, + "erlang": $fa-var-erlang, + "cotton-bureau": $fa-var-cotton-bureau, + "dashcube": $fa-var-dashcube, + "42-group": $fa-var-42-group, + "innosoft": $fa-var-innosoft, + "stack-exchange": $fa-var-stack-exchange, + "elementor": $fa-var-elementor, + "square-pied-piper": $fa-var-square-pied-piper, + "pied-piper-square": $fa-var-pied-piper-square, + "creative-commons-nd": $fa-var-creative-commons-nd, + "palfed": $fa-var-palfed, + "superpowers": $fa-var-superpowers, + "resolving": $fa-var-resolving, + "xbox": $fa-var-xbox, + "searchengin": $fa-var-searchengin, + "tiktok": $fa-var-tiktok, + "square-facebook": $fa-var-square-facebook, + "facebook-square": $fa-var-facebook-square, + "renren": $fa-var-renren, + "linux": $fa-var-linux, + "glide": $fa-var-glide, + "linkedin": $fa-var-linkedin, + "hubspot": $fa-var-hubspot, + "deploydog": $fa-var-deploydog, + "twitch": $fa-var-twitch, + "ravelry": $fa-var-ravelry, + "mixer": $fa-var-mixer, + "square-lastfm": $fa-var-square-lastfm, + "lastfm-square": $fa-var-lastfm-square, + "vimeo": $fa-var-vimeo, + "mendeley": $fa-var-mendeley, + "uniregistry": $fa-var-uniregistry, + "figma": $fa-var-figma, + "creative-commons-remix": $fa-var-creative-commons-remix, + "cc-amazon-pay": $fa-var-cc-amazon-pay, + "dropbox": $fa-var-dropbox, + "instagram": $fa-var-instagram, + "cmplid": $fa-var-cmplid, + "facebook": $fa-var-facebook, + "gripfire": $fa-var-gripfire, + "jedi-order": $fa-var-jedi-order, + "uikit": $fa-var-uikit, + "fort-awesome-alt": $fa-var-fort-awesome-alt, + "phabricator": $fa-var-phabricator, + "ussunnah": $fa-var-ussunnah, + "earlybirds": $fa-var-earlybirds, + "trade-federation": $fa-var-trade-federation, + "autoprefixer": $fa-var-autoprefixer, + "whatsapp": $fa-var-whatsapp, + "slideshare": $fa-var-slideshare, + "google-play": $fa-var-google-play, + "viadeo": $fa-var-viadeo, + "line": $fa-var-line, + "google-drive": $fa-var-google-drive, + "servicestack": $fa-var-servicestack, + "simplybuilt": $fa-var-simplybuilt, + "bitbucket": $fa-var-bitbucket, + "imdb": $fa-var-imdb, + "deezer": $fa-var-deezer, + "raspberry-pi": $fa-var-raspberry-pi, + "jira": $fa-var-jira, + "docker": $fa-var-docker, + "screenpal": $fa-var-screenpal, + "bluetooth": $fa-var-bluetooth, + "gitter": $fa-var-gitter, + "d-and-d": $fa-var-d-and-d, + "microblog": $fa-var-microblog, + "cc-diners-club": $fa-var-cc-diners-club, + "gg-circle": $fa-var-gg-circle, + "pied-piper-hat": $fa-var-pied-piper-hat, + "kickstarter-k": $fa-var-kickstarter-k, + "yandex": $fa-var-yandex, + "readme": $fa-var-readme, + "html5": $fa-var-html5, + "sellsy": $fa-var-sellsy, + "sass": $fa-var-sass, + "wirsindhandwerk": $fa-var-wirsindhandwerk, + "wsh": $fa-var-wsh, + "buromobelexperte": $fa-var-buromobelexperte, + "salesforce": $fa-var-salesforce, + "octopus-deploy": $fa-var-octopus-deploy, + "medapps": $fa-var-medapps, + "ns8": $fa-var-ns8, + "pinterest-p": $fa-var-pinterest-p, + "apper": $fa-var-apper, + "fort-awesome": $fa-var-fort-awesome, + "waze": $fa-var-waze, + "cc-jcb": $fa-var-cc-jcb, + "snapchat": $fa-var-snapchat, + "snapchat-ghost": $fa-var-snapchat-ghost, + "fantasy-flight-games": $fa-var-fantasy-flight-games, + "rust": $fa-var-rust, + "wix": $fa-var-wix, + "square-behance": $fa-var-square-behance, + "behance-square": $fa-var-behance-square, + "supple": $fa-var-supple, + "rebel": $fa-var-rebel, + "css3": $fa-var-css3, + "staylinked": $fa-var-staylinked, + "kaggle": $fa-var-kaggle, + "space-awesome": $fa-var-space-awesome, + "deviantart": $fa-var-deviantart, + "cpanel": $fa-var-cpanel, + "goodreads-g": $fa-var-goodreads-g, + "square-git": $fa-var-square-git, + "git-square": $fa-var-git-square, + "square-tumblr": $fa-var-square-tumblr, + "tumblr-square": $fa-var-tumblr-square, + "trello": $fa-var-trello, + "creative-commons-nc-jp": $fa-var-creative-commons-nc-jp, + "get-pocket": $fa-var-get-pocket, + "perbyte": $fa-var-perbyte, + "grunt": $fa-var-grunt, + "weebly": $fa-var-weebly, + "connectdevelop": $fa-var-connectdevelop, + "leanpub": $fa-var-leanpub, + "black-tie": $fa-var-black-tie, + "themeco": $fa-var-themeco, + "python": $fa-var-python, + "android": $fa-var-android, + "bots": $fa-var-bots, + "free-code-camp": $fa-var-free-code-camp, + "hornbill": $fa-var-hornbill, + "js": $fa-var-js, + "ideal": $fa-var-ideal, + "git": $fa-var-git, + "dev": $fa-var-dev, + "sketch": $fa-var-sketch, + "yandex-international": $fa-var-yandex-international, + "cc-amex": $fa-var-cc-amex, + "uber": $fa-var-uber, + "github": $fa-var-github, + "php": $fa-var-php, + "alipay": $fa-var-alipay, + "youtube": $fa-var-youtube, + "skyatlas": $fa-var-skyatlas, + "firefox-browser": $fa-var-firefox-browser, + "replyd": $fa-var-replyd, + "suse": $fa-var-suse, + "jenkins": $fa-var-jenkins, + "twitter": $fa-var-twitter, + "rockrms": $fa-var-rockrms, + "pinterest": $fa-var-pinterest, + "buffer": $fa-var-buffer, + "npm": $fa-var-npm, + "yammer": $fa-var-yammer, + "btc": $fa-var-btc, + "dribbble": $fa-var-dribbble, + "stumbleupon-circle": $fa-var-stumbleupon-circle, + "internet-explorer": $fa-var-internet-explorer, + "stubber": $fa-var-stubber, + "telegram": $fa-var-telegram, + "telegram-plane": $fa-var-telegram-plane, + "old-republic": $fa-var-old-republic, + "odysee": $fa-var-odysee, + "square-whatsapp": $fa-var-square-whatsapp, + "whatsapp-square": $fa-var-whatsapp-square, + "node-js": $fa-var-node-js, + "edge-legacy": $fa-var-edge-legacy, + "slack": $fa-var-slack, + "slack-hash": $fa-var-slack-hash, + "medrt": $fa-var-medrt, + "usb": $fa-var-usb, + "tumblr": $fa-var-tumblr, + "vaadin": $fa-var-vaadin, + "quora": $fa-var-quora, + "reacteurope": $fa-var-reacteurope, + "medium": $fa-var-medium, + "medium-m": $fa-var-medium-m, + "amilia": $fa-var-amilia, + "mixcloud": $fa-var-mixcloud, + "flipboard": $fa-var-flipboard, + "viacoin": $fa-var-viacoin, + "critical-role": $fa-var-critical-role, + "sitrox": $fa-var-sitrox, + "discourse": $fa-var-discourse, + "joomla": $fa-var-joomla, + "mastodon": $fa-var-mastodon, + "airbnb": $fa-var-airbnb, + "wolf-pack-battalion": $fa-var-wolf-pack-battalion, + "buy-n-large": $fa-var-buy-n-large, + "gulp": $fa-var-gulp, + "creative-commons-sampling-plus": $fa-var-creative-commons-sampling-plus, + "strava": $fa-var-strava, + "ember": $fa-var-ember, + "canadian-maple-leaf": $fa-var-canadian-maple-leaf, + "teamspeak": $fa-var-teamspeak, + "pushed": $fa-var-pushed, + "wordpress-simple": $fa-var-wordpress-simple, + "nutritionix": $fa-var-nutritionix, + "wodu": $fa-var-wodu, + "google-pay": $fa-var-google-pay, + "intercom": $fa-var-intercom, + "zhihu": $fa-var-zhihu, + "korvue": $fa-var-korvue, + "pix": $fa-var-pix, + "steam-symbol": $fa-var-steam-symbol, +); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/brands.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/brands.scss new file mode 100644 index 0000000..fcea462 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/brands.scss @@ -0,0 +1,30 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +@import 'functions'; +@import 'variables'; + +:root, :host { + --#{$fa-css-prefix}-style-family-brands: 'Font Awesome 6 Brands'; + --#{$fa-css-prefix}-font-brands: normal 400 1em/1 'Font Awesome 6 Brands'; +} + +@font-face { + font-family: 'Font Awesome 6 Brands'; + font-style: normal; + font-weight: 400; + font-display: $fa-font-display; + src: url('#{$fa-font-path}/fa-brands-400.woff2') format('woff2'), + url('#{$fa-font-path}/fa-brands-400.ttf') format('truetype'); +} + +.fab, +.#{$fa-css-prefix}-brands { + font-weight: 400; +} + +@each $name, $icon in $fa-brand-icons { + .#{$fa-css-prefix}-#{$name}:before { content: unquote("\"#{ $icon }\""); } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/fontawesome.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/fontawesome.scss new file mode 100644 index 0000000..ab4f133 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/fontawesome.scss @@ -0,0 +1,21 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +// Font Awesome core compile (Web Fonts-based) +// ------------------------- + +@import 'functions'; +@import 'variables'; +@import 'mixins'; +@import 'core'; +@import 'sizing'; +@import 'fixed-width'; +@import 'list'; +@import 'bordered-pulled'; +@import 'animated'; +@import 'rotated-flipped'; +@import 'stacked'; +@import 'icons'; +@import 'screen-reader'; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/regular.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/regular.scss new file mode 100644 index 0000000..40d1753 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/regular.scss @@ -0,0 +1,26 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +@import 'functions'; +@import 'variables'; + +:root, :host { + --#{$fa-css-prefix}-style-family-classic: '#{ $fa-style-family }'; + --#{$fa-css-prefix}-font-regular: normal 400 1em/1 '#{ $fa-style-family }'; +} + +@font-face { + font-family: 'Font Awesome 6 Free'; + font-style: normal; + font-weight: 400; + font-display: $fa-font-display; + src: url('#{$fa-font-path}/fa-regular-400.woff2') format('woff2'), + url('#{$fa-font-path}/fa-regular-400.ttf') format('truetype'); +} + +.far, +.#{$fa-css-prefix}-regular { + font-weight: 400; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/solid.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/solid.scss new file mode 100644 index 0000000..3dd635f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/solid.scss @@ -0,0 +1,26 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +@import 'functions'; +@import 'variables'; + +:root, :host { + --#{$fa-css-prefix}-style-family-classic: '#{ $fa-style-family }'; + --#{$fa-css-prefix}-font-solid: normal 900 1em/1 '#{ $fa-style-family }'; +} + +@font-face { + font-family: 'Font Awesome 6 Free'; + font-style: normal; + font-weight: 900; + font-display: $fa-font-display; + src: url('#{$fa-font-path}/fa-solid-900.woff2') format('woff2'), + url('#{$fa-font-path}/fa-solid-900.ttf') format('truetype'); +} + +.fas, +.#{$fa-css-prefix}-solid { + font-weight: 900; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/v4-shims.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/v4-shims.scss new file mode 100644 index 0000000..7893e7c --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/scss/vendors/fontawesome-free/v4-shims.scss @@ -0,0 +1,11 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +// V4 shims compile (Web Fonts-based) +// ------------------------- + +@import 'functions'; +@import 'variables'; +@import 'shims'; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/styles.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/styles.scss new file mode 100644 index 0000000..861d7ae --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/src/styles.scss @@ -0,0 +1,15 @@ + +@import 'scss/base/typography'; + +@import 'scss/vendors/fontawesome-free/fontawesome.scss'; +@import 'scss/vendors/fontawesome-free/brands.scss'; +@import 'scss/vendors/fontawesome-free/regular.scss'; +@import 'scss/vendors/fontawesome-free/solid.scss'; + +@import 'scss/vendors/material_io'; + +@import 'scss/shame'; + + +html, body { height: 100%; } +body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/test_and_report.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/test_and_report.bash new file mode 100644 index 0000000..5ba4c0d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/test_and_report.bash @@ -0,0 +1,3 @@ +#!/bin/bash + +ng test --no-watch --code-coverage --browsers Firefox diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/tsconfig.app.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/tsconfig.app.json new file mode 100644 index 0000000..7dc7284 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/tsconfig.app.json @@ -0,0 +1,18 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [ + "node" + ] + }, + "files": [ + "src/main.ts", + "src/main.server.ts", + "server.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/tsconfig.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/tsconfig.json new file mode 100644 index 0000000..678336b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/tsconfig.json @@ -0,0 +1,32 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "sourceMap": true, + "declaration": false, + "experimentalDecorators": true, + "moduleResolution": "node", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "useDefineForClassFields": false, + "lib": [ + "ES2022", + "dom" + ] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/tsconfig.spec.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/tsconfig.spec.json new file mode 100644 index 0000000..be7e9da --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/authorization/authorization-frontend/tsconfig.spec.json @@ -0,0 +1,14 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/docker-compose.prod.yaml b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/docker-compose.prod.yaml new file mode 100644 index 0000000..7e21ab5 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/docker-compose.prod.yaml @@ -0,0 +1,184 @@ +version: "2.4" + +services: + + ory-hydra-oauth2-example-pgadmin: + image: dpage/pgadmin4:7.3 + container_name: ory-hydra-oauth2-example-pgadmin + environment: + - PGADMIN_DEFAULT_EMAIL=admin@example.com + - PGADMIN_DEFAULT_PASSWORD=admin + volumes: + + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + + restart: unless-stopped + ports: + - 9245:80 + logging: + options: + max-size: "100m" + max-file: "1" + mem_limit: 256M + depends_on: + ory-hydra-oauth2-ex-auth-server-userdata-postgresql: + condition: service_healthy + ory-hydra-oauth2-ex-auth-server-hydradata-postgresql: + condition: service_healthy + networks: + - app-network + + ory-hydra-oauth2-example-authorization-server-backend: + image: chistousov/ory-hydra-oauth2-example-authorization-server-backend:1.0.0 + container_name: ory-hydra-oauth2-example-authorization-server-backend + environment: + - LANG=en_US.UTF-8 + - LANGUAGE=en_US:en + + - BPL_JAVA_NMT_LEVEL=detail + + # health check + - THC_PATH=/actuator/health + - THC_PORT=8090 + + - JAVA_TOOL_OPTIONS= + -Dspring.profiles.active=prod + + -Dspring.datasource.driverClassName=org.postgresql.Driver + -Dspring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect + -Dspring.datasource.url=jdbc:postgresql://user-data-postgresql:5432/user_data?user=user_data&password=${USER_DATA_POSTGRESQL_PASSWORD}&escapeSyntaxCallMode=callIfNoReturn + + -Dapplication.ory-hydra.admin.baseURI=http://ory-hydra-oauth2-example-authorization-server-hydra:4445/admin/ + + -Dapplication.ory-hydra.number-of-login-attempts=3 + + -Dapplication.ory-hydra.frontend.login-redirectURI=/login + + -Dapplication.ory-hydra.frontend.consent-redirectURI=/consent + + -Dapplication.ory-hydra.frontend.logout-redirectURI=/logout/request + -Dapplication.ory-hydra.frontend.logout-cancel-redirectURI=/logout/cancel + + user: "1005" + mem_limit: 1G + logging: + options: + max-size: "100m" + max-file: "1" + volumes: + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + healthcheck: + test: "/cnb/process/health-check" + interval: 20s + timeout: 5s + retries: 5 + start_period: 40s + depends_on: + ory-hydra-oauth2-ex-auth-server-userdata-postgresql: + condition: service_healthy + ory-hydra-oauth2-example-authorization-server-hydra: + condition: service_started + networks: + - app-network + + ory-hydra-oauth2-example-authorization-server-frontend: + image: chistousov/ory-hydra-oauth2-example-authorization-server-frontend:1.0.0 + container_name: ory-hydra-oauth2-example-authorization-server-frontend + environment: + + # health check + - THC_PATH=/health + - THC_PORT=4000 + + user: "1005" + mem_limit: 512M + logging: + options: + max-size: "100m" + max-file: "1" + volumes: + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + healthcheck: + test: "/cnb/process/health-check" + interval: 20s + timeout: 5s + retries: 5 + start_period: 40s + depends_on: + ory-hydra-oauth2-example-authorization-server-backend: + condition: service_healthy + networks: + - app-network + + ory-hydra-oauth2-example-gateway-nginx: + image: nginx:1.25.3-bookworm + container_name: ory-hydra-oauth2-example-gateway-nginx + healthcheck: + test: '[ -e /var/run/nginx.pid ] || exit 1' + interval: 20s + timeout: 5s + retries: 5 + start_period: 40s + ports: + - 443:443 + volumes: + - type: bind + source: nginx/nginx.conf + target: /etc/nginx/nginx.conf + read_only: true + + - type: bind + source: nginx/confs + target: /etc/nginx/conf.d + read_only: true + + - type: bind + source: nginx/cert + target: /cert + read_only: true + + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + + restart: unless-stopped + depends_on: + ory-hydra-oauth2-example-authorization-server-backend: + condition: service_healthy + ory-hydra-oauth2-example-authorization-server-frontend: + condition: service_healthy + logging: + options: + max-size: "100m" + max-file: "1" + mem_limit: 1G + networks: + - app-network \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/docker-compose.yaml b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/docker-compose.yaml new file mode 100644 index 0000000..3d419a3 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/docker-compose.yaml @@ -0,0 +1,177 @@ +version: "2.4" + +services: + + ory-hydra-oauth2-ex-auth-server-userdata-postgresql: + image: postgres:15.3-bookworm + environment: + - POSTGRES_USER=user_data + - POSTGRES_PASSWORD=${USER_DATA_POSTGRESQL_PASSWORD} + - POSTGRES_DB=user_data + volumes: + - type: bind + source: ./user_data/postgresql.conf + target: /etc/postgresql/postgresql.conf + read_only: true + + - type: bind + source: ./user_data/postgresql-init.sql + target: /docker-entrypoint-initdb.d/postgresql-init.sql + read_only: true + + - type: volume + source: user-data-postgresql-volume + target: /var/lib/postgresql/data + + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + restart: unless-stopped + logging: + options: + max-size: "100m" + max-file: "1" + mem_limit: 512M + command: -c 'config_file=/etc/postgresql/postgresql.conf' + healthcheck: + test: "pg_isready -U user_data" + interval: 10s + timeout: 1m + retries: 5 + networks: + app-network: + aliases: + - user-data-postgresql + + ory-hydra-oauth2-ex-auth-server-hydradata-postgresql: + image: postgres:15.3-bookworm + environment: + - POSTGRES_USER=hydra + - POSTGRES_PASSWORD=${HYDRA_POSTGRESQL_PASSWORD} + - POSTGRES_DB=hydra + volumes: + - type: bind + source: ./hydra/postgresql.conf + target: /etc/postgresql/postgresql.conf + read_only: true + + - type: volume + source: hydra-data-postgresql-volume + target: /var/lib/postgresql/data + + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + restart: unless-stopped + logging: + options: + max-size: "100m" + max-file: "1" + mem_limit: 1G + command: -c 'config_file=/etc/postgresql/postgresql.conf' + healthcheck: + test: "pg_isready -U hydra" + interval: 10s + timeout: 1m + retries: 5 + networks: + app-network: + aliases: + - hydra-data-postgresql + + ory-hydra-oauth2-example-authorization-server-hydra-migrate: + image: oryd/hydra:v2.2.0 + command: migrate sql -e --yes + restart: on-failure + environment: + # secret for cookie + - SECRETS_COOKIE=${HYDRA_SECRETS_COOKIE} + # main secret + - SECRETS_SYSTEM=${HYDRA_SECRETS_SYSTEM} + - DSN=postgres://hydra:${HYDRA_POSTGRESQL_PASSWORD}@hydra-data-postgresql:5432/hydra?sslmode=disable&max_conns=20&max_idle_conns=4 + depends_on: + ory-hydra-oauth2-ex-auth-server-hydradata-postgresql: + condition: service_healthy + volumes: + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + networks: + - app-network + + ory-hydra-oauth2-example-authorization-server-hydra: + image: oryd/hydra:v2.2.0 + command: serve all + environment: + - URLS_SELF_PUBLIC=https://authorization-server.com/ + - URLS_SELF_ADMIN=http://ory-hydra-oauth2-example-authorization-server-hydra:4445/ + - SERVE_COOKIES_SAME_SITE_MODE=Lax + - URLS_LOGIN=https://authorization-server.com/api/login + - URLS_CONSENT=https://authorization-server.com/api/consent + - URLS_LOGOUT=https://authorization-server.com/api/logout + - URLS_ERROR=https://authorization-server.com/error + # когда успешно вышел из системы + - URLS_POST_LOGOUT_REDIRECT=https://authorization-server.com/logout/success + # время жизни access_token + - TTL_ACCESS_TOKEN=1h + # время жизни refresh_token + - TTL_REFRESH_TOKEN=24h + # как долго проходит вход в систему + - TTL_LOGIN_CONSENT_REQUEST=15m + # PKCE + - OAUTH2_PKCE_ENFORCED_FOR_PUBLIC_CLIENTS=true + - OAUTH2_PKCE_ENFORCED=true + # secret for cookie + - SECRETS_COOKIE=${HYDRA_SECRETS_COOKIE} + # main secret + - SECRETS_SYSTEM=${HYDRA_SECRETS_SYSTEM} + # прокси сервер + - SERVE_TLS_ALLOW_TERMINATION_FROM=0.0.0.0/0 + - DSN=postgres://hydra:${HYDRA_POSTGRESQL_PASSWORD}@hydra-data-postgresql:5432/hydra?sslmode=disable&max_conns=20&max_idle_conns=4 + - LOG_LEVEL=trace + - SERVE_COOKIES_NAMES_LOGIN_CSRF=ory_hydra_login_csrf + + restart: unless-stopped + depends_on: + ory-hydra-oauth2-example-authorization-server-hydra-migrate: + condition: ${HYDRA_DEPENDS_ON_MIGRATE} + labels: + - docker-volume-backup.archive-pre=/bin/sh -c 'hydra janitor -e --tokens --requests --grants' + - docker-volume-backup.stop-during-backup=ory-hydra-oauth2-example-authorization-server-hydra-backup-stop-label + volumes: + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + networks: + app-network: + aliases: + - ory-hydra-oauth2-example-authorization-server-hydra + +networks: + app-network: + +volumes: + user-data-postgresql-volume: + hydra-data-postgresql-volume: diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/hydra/postgresql.conf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/hydra/postgresql.conf new file mode 100644 index 0000000..4e89674 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/hydra/postgresql.conf @@ -0,0 +1,798 @@ +# ----------------------------- +# PostgreSQL configuration file +# ----------------------------- +# +# This file consists of lines of the form: +# +# name = value +# +# (The "=" is optional.) Whitespace may be used. Comments are introduced with +# "#" anywhere on a line. The complete list of parameter names and allowed +# values can be found in the PostgreSQL documentation. +# +# The commented-out settings shown in this file represent the default values. +# Re-commenting a setting is NOT sufficient to revert it to the default value; +# you need to reload the server. +# +# This file is read on server startup and when the server receives a SIGHUP +# signal. If you edit the file on a running system, you have to SIGHUP the +# server for the changes to take effect, run "pg_ctl reload", or execute +# "SELECT pg_reload_conf()". Some parameters, which are marked below, +# require a server shutdown and restart to take effect. +# +# Any parameter can also be given as a command-line option to the server, e.g., +# "postgres -c log_connections=on". Some parameters can be changed at run time +# with the "SET" SQL command. +# +# Memory units: B = bytes Time units: us = microseconds +# kB = kilobytes ms = milliseconds +# MB = megabytes s = seconds +# GB = gigabytes min = minutes +# TB = terabytes h = hours +# d = days + + +#------------------------------------------------------------------------------ +# FILE LOCATIONS +#------------------------------------------------------------------------------ + +# The default values of these variables are driven from the -D command-line +# option or PGDATA environment variable, represented here as ConfigDir. + +#data_directory = 'ConfigDir' # use data in another directory + # (change requires restart) +#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file + # (change requires restart) +#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file + # (change requires restart) + +# If external_pid_file is not explicitly set, no extra PID file is written. +#external_pid_file = '' # write an extra PID file + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONNECTIONS AND AUTHENTICATION +#------------------------------------------------------------------------------ + +# - Connection Settings - + +listen_addresses = '*' + # comma-separated list of addresses; + # defaults to 'localhost'; use '*' for all + # (change requires restart) +#port = 5432 # (change requires restart) +#max_connections = 100 # (change requires restart) +#superuser_reserved_connections = 3 # (change requires restart) +#unix_socket_directories = '/tmp' # comma-separated list of directories + # (change requires restart) +#unix_socket_group = '' # (change requires restart) +#unix_socket_permissions = 0777 # begin with 0 to use octal notation + # (change requires restart) +#bonjour = off # advertise server via Bonjour + # (change requires restart) +#bonjour_name = '' # defaults to the computer name + # (change requires restart) + +# - TCP settings - +# see "man tcp" for details + +#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; + # 0 selects the system default +#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; + # 0 selects the system default +#tcp_keepalives_count = 0 # TCP_KEEPCNT; + # 0 selects the system default +#tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds; + # 0 selects the system default + +#client_connection_check_interval = 0 # time between checks for client + # disconnection while running queries; + # 0 for never + +# - Authentication - + +#authentication_timeout = 1min # 1s-600s +#password_encryption = scram-sha-256 # scram-sha-256 or md5 +#db_user_namespace = off + +# GSSAPI using Kerberos +#krb_server_keyfile = 'FILE:${sysconfdir}/krb5.keytab' +#krb_caseins_users = off + +# - SSL - + +#ssl = off +#ssl_ca_file = '' +#ssl_cert_file = 'server.crt' +#ssl_crl_file = '' +#ssl_crl_dir = '' +#ssl_key_file = 'server.key' +#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers +#ssl_prefer_server_ciphers = on +#ssl_ecdh_curve = 'prime256v1' +#ssl_min_protocol_version = 'TLSv1.2' +#ssl_max_protocol_version = '' +#ssl_dh_params_file = '' +#ssl_passphrase_command = '' +#ssl_passphrase_command_supports_reload = off + + +#------------------------------------------------------------------------------ +# RESOURCE USAGE (except WAL) +#------------------------------------------------------------------------------ + +# - Memory - + +#shared_buffers = 32MB # min 128kB + # (change requires restart) +#huge_pages = try # on, off, or try + # (change requires restart) +#huge_page_size = 0 # zero for system default + # (change requires restart) +#temp_buffers = 8MB # min 800kB +#max_prepared_transactions = 0 # zero disables the feature + # (change requires restart) +# Caution: it is not advisable to set max_prepared_transactions nonzero unless +# you actively intend to use prepared transactions. +#work_mem = 4MB # min 64kB +#hash_mem_multiplier = 1.0 # 1-1000.0 multiplier on hash table work_mem +#maintenance_work_mem = 64MB # min 1MB +#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem +#logical_decoding_work_mem = 64MB # min 64kB +#max_stack_depth = 2MB # min 100kB +#shared_memory_type = mmap # the default is the first option + # supported by the operating system: + # mmap + # sysv + # windows + # (change requires restart) +#dynamic_shared_memory_type = posix # the default is the first option + # supported by the operating system: + # posix + # sysv + # windows + # mmap + # (change requires restart) +#min_dynamic_shared_memory = 0MB # (change requires restart) + +# - Disk - + +#temp_file_limit = -1 # limits per-process temp file space + # in kilobytes, or -1 for no limit + +# - Kernel Resources - + +#max_files_per_process = 1000 # min 64 + # (change requires restart) + +# - Cost-Based Vacuum Delay - + +#vacuum_cost_delay = 0 # 0-100 milliseconds (0 disables) +#vacuum_cost_page_hit = 1 # 0-10000 credits +#vacuum_cost_page_miss = 2 # 0-10000 credits +#vacuum_cost_page_dirty = 20 # 0-10000 credits +#vacuum_cost_limit = 200 # 1-10000 credits + +# - Background Writer - + +#bgwriter_delay = 200ms # 10-10000ms between rounds +#bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables +#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round +#bgwriter_flush_after = 0 # measured in pages, 0 disables + +# - Asynchronous Behavior - + +#backend_flush_after = 0 # measured in pages, 0 disables +#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching +#maintenance_io_concurrency = 10 # 1-1000; 0 disables prefetching +#max_worker_processes = 8 # (change requires restart) +#max_parallel_workers_per_gather = 2 # taken from max_parallel_workers +#max_parallel_maintenance_workers = 2 # taken from max_parallel_workers +#max_parallel_workers = 8 # maximum number of max_worker_processes that + # can be used in parallel operations +#parallel_leader_participation = on +#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate + # (change requires restart) + + +#------------------------------------------------------------------------------ +# WRITE-AHEAD LOG +#------------------------------------------------------------------------------ + +# - Settings - + +#wal_level = replica # minimal, replica, or logical + # (change requires restart) +#fsync = on # flush data to disk for crash safety + # (turning this off can cause + # unrecoverable data corruption) +#synchronous_commit = on # synchronization level; + # off, local, remote_write, remote_apply, or on +#wal_sync_method = fsync # the default is the first option + # supported by the operating system: + # open_datasync + # fdatasync (default on Linux and FreeBSD) + # fsync + # fsync_writethrough + # open_sync +#full_page_writes = on # recover from partial page writes +#wal_log_hints = off # also do full page writes of non-critical updates + # (change requires restart) +#wal_compression = off # enable compression of full-page writes +#wal_init_zero = on # zero-fill new WAL files +#wal_recycle = on # recycle WAL files +#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers + # (change requires restart) +#wal_writer_delay = 200ms # 1-10000 milliseconds +#wal_writer_flush_after = 1MB # measured in pages, 0 disables +#wal_skip_threshold = 2MB + +#commit_delay = 0 # range 0-100000, in microseconds +#commit_siblings = 5 # range 1-1000 + +# - Checkpoints - + +#checkpoint_timeout = 5min # range 30s-1d +#checkpoint_completion_target = 0.9 # checkpoint target duration, 0.0 - 1.0 +#checkpoint_flush_after = 0 # measured in pages, 0 disables +#checkpoint_warning = 30s # 0 disables +#max_wal_size = 1GB +#min_wal_size = 80MB + +# - Archiving - + +#archive_mode = off # enables archiving; off, on, or always + # (change requires restart) +#archive_command = '' # command to use to archive a logfile segment + # placeholders: %p = path of file to archive + # %f = file name only + # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' +#archive_timeout = 0 # force a logfile segment switch after this + # number of seconds; 0 disables + +# - Archive Recovery - + +# These are only used in recovery mode. + +#restore_command = '' # command to use to restore an archived logfile segment + # placeholders: %p = path of file to restore + # %f = file name only + # e.g. 'cp /mnt/server/archivedir/%f %p' +#archive_cleanup_command = '' # command to execute at every restartpoint +#recovery_end_command = '' # command to execute at completion of recovery + +# - Recovery Target - + +# Set these only when performing a targeted recovery. + +#recovery_target = '' # 'immediate' to end recovery as soon as a + # consistent state is reached + # (change requires restart) +#recovery_target_name = '' # the named restore point to which recovery will proceed + # (change requires restart) +#recovery_target_time = '' # the time stamp up to which recovery will proceed + # (change requires restart) +#recovery_target_xid = '' # the transaction ID up to which recovery will proceed + # (change requires restart) +#recovery_target_lsn = '' # the WAL LSN up to which recovery will proceed + # (change requires restart) +#recovery_target_inclusive = on # Specifies whether to stop: + # just after the specified recovery target (on) + # just before the recovery target (off) + # (change requires restart) +#recovery_target_timeline = 'latest' # 'current', 'latest', or timeline ID + # (change requires restart) +#recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown' + # (change requires restart) + + +#------------------------------------------------------------------------------ +# REPLICATION +#------------------------------------------------------------------------------ + +# - Sending Servers - + +# Set these on the primary and on any standby that will send replication data. + +#max_wal_senders = 10 # max number of walsender processes + # (change requires restart) +#max_replication_slots = 10 # max number of replication slots + # (change requires restart) +#wal_keep_size = 0 # in megabytes; 0 disables +#max_slot_wal_keep_size = -1 # in megabytes; -1 disables +#wal_sender_timeout = 60s # in milliseconds; 0 disables +#track_commit_timestamp = off # collect timestamp of transaction commit + # (change requires restart) + +# - Primary Server - + +# These settings are ignored on a standby server. + +#synchronous_standby_names = '' # standby servers that provide sync rep + # method to choose sync standbys, number of sync standbys, + # and comma-separated list of application_name + # from standby(s); '*' = all +#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed + +# - Standby Servers - + +# These settings are ignored on a primary server. + +#primary_conninfo = '' # connection string to sending server +#primary_slot_name = '' # replication slot on sending server +#promote_trigger_file = '' # file name whose presence ends recovery +#hot_standby = on # "off" disallows queries during recovery + # (change requires restart) +#max_standby_archive_delay = 30s # max delay before canceling queries + # when reading WAL from archive; + # -1 allows indefinite delay +#max_standby_streaming_delay = 30s # max delay before canceling queries + # when reading streaming WAL; + # -1 allows indefinite delay +#wal_receiver_create_temp_slot = off # create temp slot if primary_slot_name + # is not set +#wal_receiver_status_interval = 10s # send replies at least this often + # 0 disables +#hot_standby_feedback = off # send info from standby to prevent + # query conflicts +#wal_receiver_timeout = 60s # time that receiver waits for + # communication from primary + # in milliseconds; 0 disables +#wal_retrieve_retry_interval = 5s # time to wait before retrying to + # retrieve WAL after a failed attempt +#recovery_min_apply_delay = 0 # minimum delay for applying changes during recovery + +# - Subscribers - + +# These settings are ignored on a publisher. + +#max_logical_replication_workers = 4 # taken from max_worker_processes + # (change requires restart) +#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers + + +#------------------------------------------------------------------------------ +# QUERY TUNING +#------------------------------------------------------------------------------ + +# - Planner Method Configuration - + +#enable_async_append = on +#enable_bitmapscan = on +#enable_gathermerge = on +#enable_hashagg = on +#enable_hashjoin = on +#enable_incremental_sort = on +#enable_indexscan = on +#enable_indexonlyscan = on +#enable_material = on +#enable_memoize = on +#enable_mergejoin = on +#enable_nestloop = on +#enable_parallel_append = on +#enable_parallel_hash = on +#enable_partition_pruning = on +#enable_partitionwise_join = off +#enable_partitionwise_aggregate = off +#enable_seqscan = on +#enable_sort = on +#enable_tidscan = on + +# - Planner Cost Constants - + +#seq_page_cost = 1.0 # measured on an arbitrary scale +#random_page_cost = 4.0 # same scale as above +#cpu_tuple_cost = 0.01 # same scale as above +#cpu_index_tuple_cost = 0.005 # same scale as above +#cpu_operator_cost = 0.0025 # same scale as above +#parallel_setup_cost = 1000.0 # same scale as above +#parallel_tuple_cost = 0.1 # same scale as above +#min_parallel_table_scan_size = 8MB +#min_parallel_index_scan_size = 512kB +#effective_cache_size = 4GB + +#jit_above_cost = 100000 # perform JIT compilation if available + # and query more expensive than this; + # -1 disables +#jit_inline_above_cost = 500000 # inline small functions if query is + # more expensive than this; -1 disables +#jit_optimize_above_cost = 500000 # use expensive JIT optimizations if + # query is more expensive than this; + # -1 disables + +# - Genetic Query Optimizer - + +#geqo = on +#geqo_threshold = 12 +#geqo_effort = 5 # range 1-10 +#geqo_pool_size = 0 # selects default based on effort +#geqo_generations = 0 # selects default based on effort +#geqo_selection_bias = 2.0 # range 1.5-2.0 +#geqo_seed = 0.0 # range 0.0-1.0 + +# - Other Planner Options - + +#default_statistics_target = 100 # range 1-10000 +#constraint_exclusion = partition # on, off, or partition +#cursor_tuple_fraction = 0.1 # range 0.0-1.0 +#from_collapse_limit = 8 +#jit = on # allow JIT compilation +#join_collapse_limit = 8 # 1 disables collapsing of explicit + # JOIN clauses +#plan_cache_mode = auto # auto, force_generic_plan or + # force_custom_plan + + +#------------------------------------------------------------------------------ +# REPORTING AND LOGGING +#------------------------------------------------------------------------------ + +# - Where to Log - + +#log_destination = 'stderr' # Valid values are combinations of + # stderr, csvlog, syslog, and eventlog, + # depending on platform. csvlog + # requires logging_collector to be on. + +# This is used when logging to stderr: +#logging_collector = off # Enable capturing of stderr and csvlog + # into log files. Required to be on for + # csvlogs. + # (change requires restart) + +# These are only used if logging_collector is on: +#log_directory = 'log' # directory where log files are written, + # can be absolute or relative to PGDATA +#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, + # can include strftime() escapes +#log_file_mode = 0600 # creation mode for log files, + # begin with 0 to use octal notation +#log_rotation_age = 1d # Automatic rotation of logfiles will + # happen after that time. 0 disables. +#log_rotation_size = 10MB # Automatic rotation of logfiles will + # happen after that much log output. + # 0 disables. +#log_truncate_on_rotation = off # If on, an existing log file with the + # same name as the new log file will be + # truncated rather than appended to. + # But such truncation only occurs on + # time-driven rotation, not on restarts + # or size-driven rotation. Default is + # off, meaning append to existing files + # in all cases. + +# These are relevant when logging to syslog: +#syslog_facility = 'LOCAL0' +#syslog_ident = 'postgres' +#syslog_sequence_numbers = on +#syslog_split_messages = on + +# This is only relevant when logging to eventlog (Windows): +# (change requires restart) +#event_source = 'PostgreSQL' + +# - When to Log - + +#log_min_messages = warning # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic + +#log_min_error_statement = error # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic (effectively off) + +#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements + # and their durations, > 0 logs only + # statements running at least this number + # of milliseconds + +#log_min_duration_sample = -1 # -1 is disabled, 0 logs a sample of statements + # and their durations, > 0 logs only a sample of + # statements running at least this number + # of milliseconds; + # sample fraction is determined by log_statement_sample_rate + +#log_statement_sample_rate = 1.0 # fraction of logged statements exceeding + # log_min_duration_sample to be logged; + # 1.0 logs all such statements, 0.0 never logs + + +#log_transaction_sample_rate = 0.0 # fraction of transactions whose statements + # are logged regardless of their duration; 1.0 logs all + # statements from all transactions, 0.0 never logs + +# - What to Log - + +#debug_print_parse = off +#debug_print_rewritten = off +#debug_print_plan = off +#debug_pretty_print = on +#log_autovacuum_min_duration = -1 # log autovacuum activity; + # -1 disables, 0 logs all actions and + # their durations, > 0 logs only + # actions running at least this number + # of milliseconds. +#log_checkpoints = off +#log_connections = off +#log_disconnections = off +#log_duration = off +#log_error_verbosity = default # terse, default, or verbose messages +#log_hostname = off +#log_line_prefix = '%m [%p] ' # special values: + # %a = application name + # %u = user name + # %d = database name + # %r = remote host and port + # %h = remote host + # %b = backend type + # %p = process ID + # %P = process ID of parallel group leader + # %t = timestamp without milliseconds + # %m = timestamp with milliseconds + # %n = timestamp with milliseconds (as a Unix epoch) + # %Q = query ID (0 if none or not computed) + # %i = command tag + # %e = SQL state + # %c = session ID + # %l = session line number + # %s = session start timestamp + # %v = virtual transaction ID + # %x = transaction ID (0 if none) + # %q = stop here in non-session + # processes + # %% = '%' + # e.g. '<%u%%%d> ' +#log_lock_waits = off # log lock waits >= deadlock_timeout +#log_recovery_conflict_waits = off # log standby recovery conflict waits + # >= deadlock_timeout +#log_parameter_max_length = -1 # when logging statements, limit logged + # bind-parameter values to N bytes; + # -1 means print in full, 0 disables +#log_parameter_max_length_on_error = 0 # when logging an error, limit logged + # bind-parameter values to N bytes; + # -1 means print in full, 0 disables +#log_statement = 'none' # none, ddl, mod, all +#log_replication_commands = off +#log_temp_files = -1 # log temporary files equal or larger + # than the specified size in kilobytes; + # -1 disables, 0 logs all temp files +#log_timezone = 'GMT' + + +#------------------------------------------------------------------------------ +# PROCESS TITLE +#------------------------------------------------------------------------------ + +#cluster_name = '' # added to process titles if nonempty + # (change requires restart) +#update_process_title = on + + +#------------------------------------------------------------------------------ +# STATISTICS +#------------------------------------------------------------------------------ + +# - Query and Index Statistics Collector - + +#track_activities = on +#track_activity_query_size = 1024 # (change requires restart) +#track_counts = on +#track_io_timing = off +#track_wal_io_timing = off +#track_functions = none # none, pl, all +#stats_temp_directory = 'pg_stat_tmp' + + +# - Monitoring - + +#compute_query_id = auto +#log_statement_stats = off +#log_parser_stats = off +#log_planner_stats = off +#log_executor_stats = off + + +#------------------------------------------------------------------------------ +# AUTOVACUUM +#------------------------------------------------------------------------------ + +#autovacuum = on # Enable autovacuum subprocess? 'on' + # requires track_counts to also be on. +#autovacuum_max_workers = 3 # max number of autovacuum subprocesses + # (change requires restart) +#autovacuum_naptime = 1min # time between autovacuum runs +#autovacuum_vacuum_threshold = 50 # min number of row updates before + # vacuum +#autovacuum_vacuum_insert_threshold = 1000 # min number of row inserts + # before vacuum; -1 disables insert + # vacuums +#autovacuum_analyze_threshold = 50 # min number of row updates before + # analyze +#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum +#autovacuum_vacuum_insert_scale_factor = 0.2 # fraction of inserts over table + # size before insert vacuum +#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze +#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum + # (change requires restart) +#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age + # before forced vacuum + # (change requires restart) +#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for + # autovacuum, in milliseconds; + # -1 means use vacuum_cost_delay +#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for + # autovacuum, -1 means use + # vacuum_cost_limit + + +#------------------------------------------------------------------------------ +# CLIENT CONNECTION DEFAULTS +#------------------------------------------------------------------------------ + +# - Statement Behavior - + +#client_min_messages = notice # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # log + # notice + # warning + # error +#search_path = '"$user", public' # schema names +#row_security = on +#default_table_access_method = 'heap' +#default_tablespace = '' # a tablespace name, '' uses the default +#default_toast_compression = 'pglz' # 'pglz' or 'lz4' +#temp_tablespaces = '' # a list of tablespace names, '' uses + # only default tablespace +#check_function_bodies = on +#default_transaction_isolation = 'read committed' +#default_transaction_read_only = off +#default_transaction_deferrable = off +#session_replication_role = 'origin' +#statement_timeout = 0 # in milliseconds, 0 is disabled +#lock_timeout = 0 # in milliseconds, 0 is disabled +#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled +#idle_session_timeout = 0 # in milliseconds, 0 is disabled +#vacuum_freeze_table_age = 150000000 +#vacuum_freeze_min_age = 50000000 +#vacuum_failsafe_age = 1600000000 +#vacuum_multixact_freeze_table_age = 150000000 +#vacuum_multixact_freeze_min_age = 5000000 +#vacuum_multixact_failsafe_age = 1600000000 +#bytea_output = 'hex' # hex, escape +#xmlbinary = 'base64' +#xmloption = 'content' +#gin_pending_list_limit = 4MB + +# - Locale and Formatting - + +#datestyle = 'iso, mdy' +#intervalstyle = 'postgres' +#timezone = 'GMT' +#timezone_abbreviations = 'Default' # Select the set of available time zone + # abbreviations. Currently, there are + # Default + # Australia (historical usage) + # India + # You can create your own file in + # share/timezonesets/. +#extra_float_digits = 1 # min -15, max 3; any value >0 actually + # selects precise output mode +#client_encoding = sql_ascii # actually, defaults to database + # encoding + +# These settings are initialized by initdb, but they can be changed. +#lc_messages = 'C' # locale for system error message + # strings +#lc_monetary = 'C' # locale for monetary formatting +#lc_numeric = 'C' # locale for number formatting +#lc_time = 'C' # locale for time formatting + +# default configuration for text search +#default_text_search_config = 'pg_catalog.simple' + +# - Shared Library Preloading - + +#local_preload_libraries = '' +#session_preload_libraries = '' +#shared_preload_libraries = '' # (change requires restart) +#jit_provider = 'llvmjit' # JIT library to use + +# - Other Defaults - + +#dynamic_library_path = '$libdir' +#extension_destdir = '' # prepend path when loading extensions + # and shared objects (added by Debian) +#gin_fuzzy_search_limit = 0 + + +#------------------------------------------------------------------------------ +# LOCK MANAGEMENT +#------------------------------------------------------------------------------ + +#deadlock_timeout = 1s +#max_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_relation = -2 # negative values mean + # (max_pred_locks_per_transaction + # / -max_pred_locks_per_relation) - 1 +#max_pred_locks_per_page = 2 # min 0 + + +#------------------------------------------------------------------------------ +# VERSION AND PLATFORM COMPATIBILITY +#------------------------------------------------------------------------------ + +# - Previous PostgreSQL Versions - + +#array_nulls = on +#backslash_quote = safe_encoding # on, off, or safe_encoding +#escape_string_warning = on +#lo_compat_privileges = off +#quote_all_identifiers = off +#standard_conforming_strings = on +#synchronize_seqscans = on + +# - Other Platforms and Clients - + +#transform_null_equals = off + + +#------------------------------------------------------------------------------ +# ERROR HANDLING +#------------------------------------------------------------------------------ + +#exit_on_error = off # terminate session on any error? +#restart_after_crash = on # reinitialize after backend crash? +#data_sync_retry = off # retry or panic on failure to fsync + # data? + # (change requires restart) +#recovery_init_sync_method = fsync # fsync, syncfs (Linux 5.8+) + + +#------------------------------------------------------------------------------ +# CONFIG FILE INCLUDES +#------------------------------------------------------------------------------ + +# These options allow settings to be loaded from files other than the +# default postgresql.conf. Note that these are directives, not variable +# assignments, so they can usefully be given more than once. + +#include_dir = '...' # include files ending in '.conf' from + # a directory, e.g., 'conf.d' +#include_if_exists = '...' # include file only if it exists +#include = '...' # include file + + +#------------------------------------------------------------------------------ +# CUSTOMIZED OPTIONS +#------------------------------------------------------------------------------ + +# Add settings for extensions here diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/nginx/confs/.gitignore b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/nginx/confs/.gitignore new file mode 100644 index 0000000..63b2c4f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/nginx/confs/.gitignore @@ -0,0 +1 @@ +authorization-server.conf \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/nginx/confs/authorization-server.conf.templ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/nginx/confs/authorization-server.conf.templ new file mode 100644 index 0000000..fabc784 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/nginx/confs/authorization-server.conf.templ @@ -0,0 +1,83 @@ +server { + listen 443 ssl; + http2 on; + server_name ${DNS_AUTHORIZATION_SERVER}; + + ssl_certificate /cert/${DNS_AUTHORIZATION_SERVER}/${DNS_AUTHORIZATION_SERVER}.crt; + ssl_certificate_key /cert/${DNS_AUTHORIZATION_SERVER}/${DNS_AUTHORIZATION_SERVER}.key; + + add_header Strict-Transport-Security "max-age=31536000; preload" always; + add_header X-Content-Type-Options "nosniff"; + add_header X-Frame-Options "DENY"; + add_header X-XSS-Protection "1; mode=block"; + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + # Docker DNS + resolver 127.0.0.11; + + location ~ ^/(.well-known|oauth2/auth|oauth2/token|oauth2/revoke|oauth2/fallbacks/consent|oauth2/fallbacks/error|userinfo|oauth2/sessions/logout)/? { + set $ory_hydra_public_hydra http://ory-hydra-oauth2-example-authorization-server-hydra:4444; + proxy_pass $ory_hydra_public_hydra; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto-Version $http2; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /; + } + location ~ ^/(admin/clients|admin/oauth2/introspect)/? { + set $ory_hydra_admin_hydra http://ory-hydra-oauth2-example-authorization-server-hydra:4445; + proxy_pass $ory_hydra_admin_hydra; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto-Version $http2; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /; + + # Auth Basic + auth_basic "Ory Hydra Introspect Area"; + auth_basic_user_file /etc/nginx/conf.d/htpasswd_introspect; + } + + location /api/ { + set $ory_hydra_oauth2_example_authorization_server_backend http://ory-hydra-oauth2-example-authorization-server-backend:8090; + rewrite /api/(.*) /$1 break; + proxy_pass $ory_hydra_oauth2_example_authorization_server_backend; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto-Version $http2; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /api; + } + location / { + set $ory_hydra_oauth2_example_authorization_server_frontend http://ory-hydra-oauth2-example-authorization-server-frontend:4000; + rewrite /(.*) /$1 break; + proxy_pass $ory_hydra_oauth2_example_authorization_server_frontend; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto-Version $http2; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /; + } + + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/nginx/confs/default.conf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/nginx/confs/default.conf new file mode 100644 index 0000000..86124f6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/nginx/confs/default.conf @@ -0,0 +1,11 @@ +server { + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/nginx/nginx.conf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/nginx/nginx.conf new file mode 100644 index 0000000..90aac41 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/nginx/nginx.conf @@ -0,0 +1,36 @@ +user nginx; +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + variables_hash_bucket_size 512; + + proxy_buffer_size 128k; + proxy_buffers 4 256k; + proxy_busy_buffers_size 256k; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/user_data/postgresql-init.sql b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/user_data/postgresql-init.sql new file mode 100644 index 0000000..820e27b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/user_data/postgresql-init.sql @@ -0,0 +1,73 @@ +DO +' +declare +begin + + -- SCHEMA: horns_and_hooves + CREATE SCHEMA IF NOT EXISTS horns_and_hooves; + + -- Table Users + CREATE TABLE IF NOT EXISTS horns_and_hooves.user + ( + id bigserial NOT NULL, + login text NOT NULL, + password text NOT NULL, + org_name text NOT NULL, + CONSTRAINT "PK_user" PRIMARY KEY (id), + CONSTRAINT "UNIQUE_login" UNIQUE (login), + CONSTRAINT "UNIQUE_org_name" UNIQUE (org_name) + ); + + COMMENT ON TABLE horns_and_hooves.user IS ''Table with users''; + + -- Procedures + +CREATE OR REPLACE PROCEDURE horns_and_hooves.add_user( + IN login horns_and_hooves.user.login%type, + IN password horns_and_hooves.user.password%type, + IN org_name horns_and_hooves.user.org_name%type, + OUT user_id horns_and_hooves.user.id%type +) +AS +$BODY$ +BEGIN +INSERT INTO horns_and_hooves.user (login, password, org_name) VALUES (login, password, org_name) RETURNING id INTO user_id; +END +$BODY$ LANGUAGE plpgsql; + +COMMENT ON PROCEDURE horns_and_hooves.add_user( + login horns_and_hooves.user.login%type, + password horns_and_hooves.user.password%type, + org_name horns_and_hooves.user.org_name%type, + OUT user_id horns_and_hooves.user.id%type + ) + IS ''Add new user +Params: in login - new user login + in password - new user password + in org_name - new user organization + out user_id - new user ID +Output: none''; + + + +CREATE OR REPLACE FUNCTION horns_and_hooves.get_login( + IN ref_login horns_and_hooves.user.login%type +) +RETURNS REFCURSOR AS +$BODY$ +DECLARE + ref_cursor REFCURSOR; +BEGIN + + OPEN ref_cursor FOR + SELECT u.* + FROM horns_and_hooves.user u + WHERE u.login = ref_login; + + RETURN ref_cursor; + +END +$BODY$ LANGUAGE plpgsql; + +end; +' LANGUAGE PLPGSQL; \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/user_data/postgresql.conf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/user_data/postgresql.conf new file mode 100644 index 0000000..4e89674 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_authorization_server/user_data/postgresql.conf @@ -0,0 +1,798 @@ +# ----------------------------- +# PostgreSQL configuration file +# ----------------------------- +# +# This file consists of lines of the form: +# +# name = value +# +# (The "=" is optional.) Whitespace may be used. Comments are introduced with +# "#" anywhere on a line. The complete list of parameter names and allowed +# values can be found in the PostgreSQL documentation. +# +# The commented-out settings shown in this file represent the default values. +# Re-commenting a setting is NOT sufficient to revert it to the default value; +# you need to reload the server. +# +# This file is read on server startup and when the server receives a SIGHUP +# signal. If you edit the file on a running system, you have to SIGHUP the +# server for the changes to take effect, run "pg_ctl reload", or execute +# "SELECT pg_reload_conf()". Some parameters, which are marked below, +# require a server shutdown and restart to take effect. +# +# Any parameter can also be given as a command-line option to the server, e.g., +# "postgres -c log_connections=on". Some parameters can be changed at run time +# with the "SET" SQL command. +# +# Memory units: B = bytes Time units: us = microseconds +# kB = kilobytes ms = milliseconds +# MB = megabytes s = seconds +# GB = gigabytes min = minutes +# TB = terabytes h = hours +# d = days + + +#------------------------------------------------------------------------------ +# FILE LOCATIONS +#------------------------------------------------------------------------------ + +# The default values of these variables are driven from the -D command-line +# option or PGDATA environment variable, represented here as ConfigDir. + +#data_directory = 'ConfigDir' # use data in another directory + # (change requires restart) +#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file + # (change requires restart) +#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file + # (change requires restart) + +# If external_pid_file is not explicitly set, no extra PID file is written. +#external_pid_file = '' # write an extra PID file + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONNECTIONS AND AUTHENTICATION +#------------------------------------------------------------------------------ + +# - Connection Settings - + +listen_addresses = '*' + # comma-separated list of addresses; + # defaults to 'localhost'; use '*' for all + # (change requires restart) +#port = 5432 # (change requires restart) +#max_connections = 100 # (change requires restart) +#superuser_reserved_connections = 3 # (change requires restart) +#unix_socket_directories = '/tmp' # comma-separated list of directories + # (change requires restart) +#unix_socket_group = '' # (change requires restart) +#unix_socket_permissions = 0777 # begin with 0 to use octal notation + # (change requires restart) +#bonjour = off # advertise server via Bonjour + # (change requires restart) +#bonjour_name = '' # defaults to the computer name + # (change requires restart) + +# - TCP settings - +# see "man tcp" for details + +#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; + # 0 selects the system default +#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; + # 0 selects the system default +#tcp_keepalives_count = 0 # TCP_KEEPCNT; + # 0 selects the system default +#tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds; + # 0 selects the system default + +#client_connection_check_interval = 0 # time between checks for client + # disconnection while running queries; + # 0 for never + +# - Authentication - + +#authentication_timeout = 1min # 1s-600s +#password_encryption = scram-sha-256 # scram-sha-256 or md5 +#db_user_namespace = off + +# GSSAPI using Kerberos +#krb_server_keyfile = 'FILE:${sysconfdir}/krb5.keytab' +#krb_caseins_users = off + +# - SSL - + +#ssl = off +#ssl_ca_file = '' +#ssl_cert_file = 'server.crt' +#ssl_crl_file = '' +#ssl_crl_dir = '' +#ssl_key_file = 'server.key' +#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers +#ssl_prefer_server_ciphers = on +#ssl_ecdh_curve = 'prime256v1' +#ssl_min_protocol_version = 'TLSv1.2' +#ssl_max_protocol_version = '' +#ssl_dh_params_file = '' +#ssl_passphrase_command = '' +#ssl_passphrase_command_supports_reload = off + + +#------------------------------------------------------------------------------ +# RESOURCE USAGE (except WAL) +#------------------------------------------------------------------------------ + +# - Memory - + +#shared_buffers = 32MB # min 128kB + # (change requires restart) +#huge_pages = try # on, off, or try + # (change requires restart) +#huge_page_size = 0 # zero for system default + # (change requires restart) +#temp_buffers = 8MB # min 800kB +#max_prepared_transactions = 0 # zero disables the feature + # (change requires restart) +# Caution: it is not advisable to set max_prepared_transactions nonzero unless +# you actively intend to use prepared transactions. +#work_mem = 4MB # min 64kB +#hash_mem_multiplier = 1.0 # 1-1000.0 multiplier on hash table work_mem +#maintenance_work_mem = 64MB # min 1MB +#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem +#logical_decoding_work_mem = 64MB # min 64kB +#max_stack_depth = 2MB # min 100kB +#shared_memory_type = mmap # the default is the first option + # supported by the operating system: + # mmap + # sysv + # windows + # (change requires restart) +#dynamic_shared_memory_type = posix # the default is the first option + # supported by the operating system: + # posix + # sysv + # windows + # mmap + # (change requires restart) +#min_dynamic_shared_memory = 0MB # (change requires restart) + +# - Disk - + +#temp_file_limit = -1 # limits per-process temp file space + # in kilobytes, or -1 for no limit + +# - Kernel Resources - + +#max_files_per_process = 1000 # min 64 + # (change requires restart) + +# - Cost-Based Vacuum Delay - + +#vacuum_cost_delay = 0 # 0-100 milliseconds (0 disables) +#vacuum_cost_page_hit = 1 # 0-10000 credits +#vacuum_cost_page_miss = 2 # 0-10000 credits +#vacuum_cost_page_dirty = 20 # 0-10000 credits +#vacuum_cost_limit = 200 # 1-10000 credits + +# - Background Writer - + +#bgwriter_delay = 200ms # 10-10000ms between rounds +#bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables +#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round +#bgwriter_flush_after = 0 # measured in pages, 0 disables + +# - Asynchronous Behavior - + +#backend_flush_after = 0 # measured in pages, 0 disables +#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching +#maintenance_io_concurrency = 10 # 1-1000; 0 disables prefetching +#max_worker_processes = 8 # (change requires restart) +#max_parallel_workers_per_gather = 2 # taken from max_parallel_workers +#max_parallel_maintenance_workers = 2 # taken from max_parallel_workers +#max_parallel_workers = 8 # maximum number of max_worker_processes that + # can be used in parallel operations +#parallel_leader_participation = on +#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate + # (change requires restart) + + +#------------------------------------------------------------------------------ +# WRITE-AHEAD LOG +#------------------------------------------------------------------------------ + +# - Settings - + +#wal_level = replica # minimal, replica, or logical + # (change requires restart) +#fsync = on # flush data to disk for crash safety + # (turning this off can cause + # unrecoverable data corruption) +#synchronous_commit = on # synchronization level; + # off, local, remote_write, remote_apply, or on +#wal_sync_method = fsync # the default is the first option + # supported by the operating system: + # open_datasync + # fdatasync (default on Linux and FreeBSD) + # fsync + # fsync_writethrough + # open_sync +#full_page_writes = on # recover from partial page writes +#wal_log_hints = off # also do full page writes of non-critical updates + # (change requires restart) +#wal_compression = off # enable compression of full-page writes +#wal_init_zero = on # zero-fill new WAL files +#wal_recycle = on # recycle WAL files +#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers + # (change requires restart) +#wal_writer_delay = 200ms # 1-10000 milliseconds +#wal_writer_flush_after = 1MB # measured in pages, 0 disables +#wal_skip_threshold = 2MB + +#commit_delay = 0 # range 0-100000, in microseconds +#commit_siblings = 5 # range 1-1000 + +# - Checkpoints - + +#checkpoint_timeout = 5min # range 30s-1d +#checkpoint_completion_target = 0.9 # checkpoint target duration, 0.0 - 1.0 +#checkpoint_flush_after = 0 # measured in pages, 0 disables +#checkpoint_warning = 30s # 0 disables +#max_wal_size = 1GB +#min_wal_size = 80MB + +# - Archiving - + +#archive_mode = off # enables archiving; off, on, or always + # (change requires restart) +#archive_command = '' # command to use to archive a logfile segment + # placeholders: %p = path of file to archive + # %f = file name only + # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' +#archive_timeout = 0 # force a logfile segment switch after this + # number of seconds; 0 disables + +# - Archive Recovery - + +# These are only used in recovery mode. + +#restore_command = '' # command to use to restore an archived logfile segment + # placeholders: %p = path of file to restore + # %f = file name only + # e.g. 'cp /mnt/server/archivedir/%f %p' +#archive_cleanup_command = '' # command to execute at every restartpoint +#recovery_end_command = '' # command to execute at completion of recovery + +# - Recovery Target - + +# Set these only when performing a targeted recovery. + +#recovery_target = '' # 'immediate' to end recovery as soon as a + # consistent state is reached + # (change requires restart) +#recovery_target_name = '' # the named restore point to which recovery will proceed + # (change requires restart) +#recovery_target_time = '' # the time stamp up to which recovery will proceed + # (change requires restart) +#recovery_target_xid = '' # the transaction ID up to which recovery will proceed + # (change requires restart) +#recovery_target_lsn = '' # the WAL LSN up to which recovery will proceed + # (change requires restart) +#recovery_target_inclusive = on # Specifies whether to stop: + # just after the specified recovery target (on) + # just before the recovery target (off) + # (change requires restart) +#recovery_target_timeline = 'latest' # 'current', 'latest', or timeline ID + # (change requires restart) +#recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown' + # (change requires restart) + + +#------------------------------------------------------------------------------ +# REPLICATION +#------------------------------------------------------------------------------ + +# - Sending Servers - + +# Set these on the primary and on any standby that will send replication data. + +#max_wal_senders = 10 # max number of walsender processes + # (change requires restart) +#max_replication_slots = 10 # max number of replication slots + # (change requires restart) +#wal_keep_size = 0 # in megabytes; 0 disables +#max_slot_wal_keep_size = -1 # in megabytes; -1 disables +#wal_sender_timeout = 60s # in milliseconds; 0 disables +#track_commit_timestamp = off # collect timestamp of transaction commit + # (change requires restart) + +# - Primary Server - + +# These settings are ignored on a standby server. + +#synchronous_standby_names = '' # standby servers that provide sync rep + # method to choose sync standbys, number of sync standbys, + # and comma-separated list of application_name + # from standby(s); '*' = all +#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed + +# - Standby Servers - + +# These settings are ignored on a primary server. + +#primary_conninfo = '' # connection string to sending server +#primary_slot_name = '' # replication slot on sending server +#promote_trigger_file = '' # file name whose presence ends recovery +#hot_standby = on # "off" disallows queries during recovery + # (change requires restart) +#max_standby_archive_delay = 30s # max delay before canceling queries + # when reading WAL from archive; + # -1 allows indefinite delay +#max_standby_streaming_delay = 30s # max delay before canceling queries + # when reading streaming WAL; + # -1 allows indefinite delay +#wal_receiver_create_temp_slot = off # create temp slot if primary_slot_name + # is not set +#wal_receiver_status_interval = 10s # send replies at least this often + # 0 disables +#hot_standby_feedback = off # send info from standby to prevent + # query conflicts +#wal_receiver_timeout = 60s # time that receiver waits for + # communication from primary + # in milliseconds; 0 disables +#wal_retrieve_retry_interval = 5s # time to wait before retrying to + # retrieve WAL after a failed attempt +#recovery_min_apply_delay = 0 # minimum delay for applying changes during recovery + +# - Subscribers - + +# These settings are ignored on a publisher. + +#max_logical_replication_workers = 4 # taken from max_worker_processes + # (change requires restart) +#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers + + +#------------------------------------------------------------------------------ +# QUERY TUNING +#------------------------------------------------------------------------------ + +# - Planner Method Configuration - + +#enable_async_append = on +#enable_bitmapscan = on +#enable_gathermerge = on +#enable_hashagg = on +#enable_hashjoin = on +#enable_incremental_sort = on +#enable_indexscan = on +#enable_indexonlyscan = on +#enable_material = on +#enable_memoize = on +#enable_mergejoin = on +#enable_nestloop = on +#enable_parallel_append = on +#enable_parallel_hash = on +#enable_partition_pruning = on +#enable_partitionwise_join = off +#enable_partitionwise_aggregate = off +#enable_seqscan = on +#enable_sort = on +#enable_tidscan = on + +# - Planner Cost Constants - + +#seq_page_cost = 1.0 # measured on an arbitrary scale +#random_page_cost = 4.0 # same scale as above +#cpu_tuple_cost = 0.01 # same scale as above +#cpu_index_tuple_cost = 0.005 # same scale as above +#cpu_operator_cost = 0.0025 # same scale as above +#parallel_setup_cost = 1000.0 # same scale as above +#parallel_tuple_cost = 0.1 # same scale as above +#min_parallel_table_scan_size = 8MB +#min_parallel_index_scan_size = 512kB +#effective_cache_size = 4GB + +#jit_above_cost = 100000 # perform JIT compilation if available + # and query more expensive than this; + # -1 disables +#jit_inline_above_cost = 500000 # inline small functions if query is + # more expensive than this; -1 disables +#jit_optimize_above_cost = 500000 # use expensive JIT optimizations if + # query is more expensive than this; + # -1 disables + +# - Genetic Query Optimizer - + +#geqo = on +#geqo_threshold = 12 +#geqo_effort = 5 # range 1-10 +#geqo_pool_size = 0 # selects default based on effort +#geqo_generations = 0 # selects default based on effort +#geqo_selection_bias = 2.0 # range 1.5-2.0 +#geqo_seed = 0.0 # range 0.0-1.0 + +# - Other Planner Options - + +#default_statistics_target = 100 # range 1-10000 +#constraint_exclusion = partition # on, off, or partition +#cursor_tuple_fraction = 0.1 # range 0.0-1.0 +#from_collapse_limit = 8 +#jit = on # allow JIT compilation +#join_collapse_limit = 8 # 1 disables collapsing of explicit + # JOIN clauses +#plan_cache_mode = auto # auto, force_generic_plan or + # force_custom_plan + + +#------------------------------------------------------------------------------ +# REPORTING AND LOGGING +#------------------------------------------------------------------------------ + +# - Where to Log - + +#log_destination = 'stderr' # Valid values are combinations of + # stderr, csvlog, syslog, and eventlog, + # depending on platform. csvlog + # requires logging_collector to be on. + +# This is used when logging to stderr: +#logging_collector = off # Enable capturing of stderr and csvlog + # into log files. Required to be on for + # csvlogs. + # (change requires restart) + +# These are only used if logging_collector is on: +#log_directory = 'log' # directory where log files are written, + # can be absolute or relative to PGDATA +#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, + # can include strftime() escapes +#log_file_mode = 0600 # creation mode for log files, + # begin with 0 to use octal notation +#log_rotation_age = 1d # Automatic rotation of logfiles will + # happen after that time. 0 disables. +#log_rotation_size = 10MB # Automatic rotation of logfiles will + # happen after that much log output. + # 0 disables. +#log_truncate_on_rotation = off # If on, an existing log file with the + # same name as the new log file will be + # truncated rather than appended to. + # But such truncation only occurs on + # time-driven rotation, not on restarts + # or size-driven rotation. Default is + # off, meaning append to existing files + # in all cases. + +# These are relevant when logging to syslog: +#syslog_facility = 'LOCAL0' +#syslog_ident = 'postgres' +#syslog_sequence_numbers = on +#syslog_split_messages = on + +# This is only relevant when logging to eventlog (Windows): +# (change requires restart) +#event_source = 'PostgreSQL' + +# - When to Log - + +#log_min_messages = warning # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic + +#log_min_error_statement = error # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic (effectively off) + +#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements + # and their durations, > 0 logs only + # statements running at least this number + # of milliseconds + +#log_min_duration_sample = -1 # -1 is disabled, 0 logs a sample of statements + # and their durations, > 0 logs only a sample of + # statements running at least this number + # of milliseconds; + # sample fraction is determined by log_statement_sample_rate + +#log_statement_sample_rate = 1.0 # fraction of logged statements exceeding + # log_min_duration_sample to be logged; + # 1.0 logs all such statements, 0.0 never logs + + +#log_transaction_sample_rate = 0.0 # fraction of transactions whose statements + # are logged regardless of their duration; 1.0 logs all + # statements from all transactions, 0.0 never logs + +# - What to Log - + +#debug_print_parse = off +#debug_print_rewritten = off +#debug_print_plan = off +#debug_pretty_print = on +#log_autovacuum_min_duration = -1 # log autovacuum activity; + # -1 disables, 0 logs all actions and + # their durations, > 0 logs only + # actions running at least this number + # of milliseconds. +#log_checkpoints = off +#log_connections = off +#log_disconnections = off +#log_duration = off +#log_error_verbosity = default # terse, default, or verbose messages +#log_hostname = off +#log_line_prefix = '%m [%p] ' # special values: + # %a = application name + # %u = user name + # %d = database name + # %r = remote host and port + # %h = remote host + # %b = backend type + # %p = process ID + # %P = process ID of parallel group leader + # %t = timestamp without milliseconds + # %m = timestamp with milliseconds + # %n = timestamp with milliseconds (as a Unix epoch) + # %Q = query ID (0 if none or not computed) + # %i = command tag + # %e = SQL state + # %c = session ID + # %l = session line number + # %s = session start timestamp + # %v = virtual transaction ID + # %x = transaction ID (0 if none) + # %q = stop here in non-session + # processes + # %% = '%' + # e.g. '<%u%%%d> ' +#log_lock_waits = off # log lock waits >= deadlock_timeout +#log_recovery_conflict_waits = off # log standby recovery conflict waits + # >= deadlock_timeout +#log_parameter_max_length = -1 # when logging statements, limit logged + # bind-parameter values to N bytes; + # -1 means print in full, 0 disables +#log_parameter_max_length_on_error = 0 # when logging an error, limit logged + # bind-parameter values to N bytes; + # -1 means print in full, 0 disables +#log_statement = 'none' # none, ddl, mod, all +#log_replication_commands = off +#log_temp_files = -1 # log temporary files equal or larger + # than the specified size in kilobytes; + # -1 disables, 0 logs all temp files +#log_timezone = 'GMT' + + +#------------------------------------------------------------------------------ +# PROCESS TITLE +#------------------------------------------------------------------------------ + +#cluster_name = '' # added to process titles if nonempty + # (change requires restart) +#update_process_title = on + + +#------------------------------------------------------------------------------ +# STATISTICS +#------------------------------------------------------------------------------ + +# - Query and Index Statistics Collector - + +#track_activities = on +#track_activity_query_size = 1024 # (change requires restart) +#track_counts = on +#track_io_timing = off +#track_wal_io_timing = off +#track_functions = none # none, pl, all +#stats_temp_directory = 'pg_stat_tmp' + + +# - Monitoring - + +#compute_query_id = auto +#log_statement_stats = off +#log_parser_stats = off +#log_planner_stats = off +#log_executor_stats = off + + +#------------------------------------------------------------------------------ +# AUTOVACUUM +#------------------------------------------------------------------------------ + +#autovacuum = on # Enable autovacuum subprocess? 'on' + # requires track_counts to also be on. +#autovacuum_max_workers = 3 # max number of autovacuum subprocesses + # (change requires restart) +#autovacuum_naptime = 1min # time between autovacuum runs +#autovacuum_vacuum_threshold = 50 # min number of row updates before + # vacuum +#autovacuum_vacuum_insert_threshold = 1000 # min number of row inserts + # before vacuum; -1 disables insert + # vacuums +#autovacuum_analyze_threshold = 50 # min number of row updates before + # analyze +#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum +#autovacuum_vacuum_insert_scale_factor = 0.2 # fraction of inserts over table + # size before insert vacuum +#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze +#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum + # (change requires restart) +#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age + # before forced vacuum + # (change requires restart) +#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for + # autovacuum, in milliseconds; + # -1 means use vacuum_cost_delay +#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for + # autovacuum, -1 means use + # vacuum_cost_limit + + +#------------------------------------------------------------------------------ +# CLIENT CONNECTION DEFAULTS +#------------------------------------------------------------------------------ + +# - Statement Behavior - + +#client_min_messages = notice # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # log + # notice + # warning + # error +#search_path = '"$user", public' # schema names +#row_security = on +#default_table_access_method = 'heap' +#default_tablespace = '' # a tablespace name, '' uses the default +#default_toast_compression = 'pglz' # 'pglz' or 'lz4' +#temp_tablespaces = '' # a list of tablespace names, '' uses + # only default tablespace +#check_function_bodies = on +#default_transaction_isolation = 'read committed' +#default_transaction_read_only = off +#default_transaction_deferrable = off +#session_replication_role = 'origin' +#statement_timeout = 0 # in milliseconds, 0 is disabled +#lock_timeout = 0 # in milliseconds, 0 is disabled +#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled +#idle_session_timeout = 0 # in milliseconds, 0 is disabled +#vacuum_freeze_table_age = 150000000 +#vacuum_freeze_min_age = 50000000 +#vacuum_failsafe_age = 1600000000 +#vacuum_multixact_freeze_table_age = 150000000 +#vacuum_multixact_freeze_min_age = 5000000 +#vacuum_multixact_failsafe_age = 1600000000 +#bytea_output = 'hex' # hex, escape +#xmlbinary = 'base64' +#xmloption = 'content' +#gin_pending_list_limit = 4MB + +# - Locale and Formatting - + +#datestyle = 'iso, mdy' +#intervalstyle = 'postgres' +#timezone = 'GMT' +#timezone_abbreviations = 'Default' # Select the set of available time zone + # abbreviations. Currently, there are + # Default + # Australia (historical usage) + # India + # You can create your own file in + # share/timezonesets/. +#extra_float_digits = 1 # min -15, max 3; any value >0 actually + # selects precise output mode +#client_encoding = sql_ascii # actually, defaults to database + # encoding + +# These settings are initialized by initdb, but they can be changed. +#lc_messages = 'C' # locale for system error message + # strings +#lc_monetary = 'C' # locale for monetary formatting +#lc_numeric = 'C' # locale for number formatting +#lc_time = 'C' # locale for time formatting + +# default configuration for text search +#default_text_search_config = 'pg_catalog.simple' + +# - Shared Library Preloading - + +#local_preload_libraries = '' +#session_preload_libraries = '' +#shared_preload_libraries = '' # (change requires restart) +#jit_provider = 'llvmjit' # JIT library to use + +# - Other Defaults - + +#dynamic_library_path = '$libdir' +#extension_destdir = '' # prepend path when loading extensions + # and shared objects (added by Debian) +#gin_fuzzy_search_limit = 0 + + +#------------------------------------------------------------------------------ +# LOCK MANAGEMENT +#------------------------------------------------------------------------------ + +#deadlock_timeout = 1s +#max_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_relation = -2 # negative values mean + # (max_pred_locks_per_transaction + # / -max_pred_locks_per_relation) - 1 +#max_pred_locks_per_page = 2 # min 0 + + +#------------------------------------------------------------------------------ +# VERSION AND PLATFORM COMPATIBILITY +#------------------------------------------------------------------------------ + +# - Previous PostgreSQL Versions - + +#array_nulls = on +#backslash_quote = safe_encoding # on, off, or safe_encoding +#escape_string_warning = on +#lo_compat_privileges = off +#quote_all_identifiers = off +#standard_conforming_strings = on +#synchronize_seqscans = on + +# - Other Platforms and Clients - + +#transform_null_equals = off + + +#------------------------------------------------------------------------------ +# ERROR HANDLING +#------------------------------------------------------------------------------ + +#exit_on_error = off # terminate session on any error? +#restart_after_crash = on # reinitialize after backend crash? +#data_sync_retry = off # retry or panic on failure to fsync + # data? + # (change requires restart) +#recovery_init_sync_method = fsync # fsync, syncfs (Linux 5.8+) + + +#------------------------------------------------------------------------------ +# CONFIG FILE INCLUDES +#------------------------------------------------------------------------------ + +# These options allow settings to be loaded from files other than the +# default postgresql.conf. Note that these are directives, not variable +# assignments, so they can usefully be given more than once. + +#include_dir = '...' # include files ending in '.conf' from + # a directory, e.g., 'conf.d' +#include_if_exists = '...' # include file only if it exists +#include = '...' # include file + + +#------------------------------------------------------------------------------ +# CUSTOMIZED OPTIONS +#------------------------------------------------------------------------------ + +# Add settings for extensions here diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/.gitignore b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/.gitignore new file mode 100644 index 0000000..839bdac --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/.gitignore @@ -0,0 +1,7 @@ +.env + +**/ca.key +**/ca.crt +**/ca.srl + +nginx/cert \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/ca-certificates/type b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/ca-certificates/type new file mode 100644 index 0000000..54619ed --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/ca-certificates/type @@ -0,0 +1 @@ +ca-certificates \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/docker-compose.yaml b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/docker-compose.yaml new file mode 100644 index 0000000..d3dcb26 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/docker-compose.yaml @@ -0,0 +1,159 @@ +version: "2.4" + +services: + + ory-hydra-oauth2-example-client-readonly-backend: + image: chistousov/ory-hydra-oauth2-example-client-readonly-backend:1.0.0 + container_name: ory-hydra-oauth2-example-client-readonly-backend + environment: + - LANG=en_US.UTF-8 + - LANGUAGE=en_US:en + + - BPL_JAVA_NMT_LEVEL=detail + + # health check + - THC_PATH=/actuator/health + - THC_PORT=8080 + + # ca add + - SERVICE_BINDING_ROOT=/bindings + + - JAVA_TOOL_OPTIONS= + -Dspring.profiles.active=prod + + -Dspring.security.oauth2.client.registration.client-readonly.client-id=${CLIENT_READONLY_CLIENT_ID} + -Dspring.security.oauth2.client.registration.client-readonly.client-secret=${CLIENT_READONLY_CLIENT_SECRET} + -Dspring.security.oauth2.client.registration.client-readonly.redirect-uri=https://${DNS_CLIENT_READONLY}/api/login/oauth2/code/client-readonly + -Dspring.security.oauth2.client.registration.client-readonly.scope="read" + + -Dspring.security.oauth2.client.provider.spring.authorization-uri=https://${DNS_AUTHORIZATION_SERVER}/oauth2/auth + -Dspring.security.oauth2.client.provider.spring.token-uri=https://${DNS_AUTHORIZATION_SERVER}/oauth2/token + -Dspring.security.oauth2.client.provider.spring.user-info-uri=https://${DNS_AUTHORIZATION_SERVER}/userinfo + -Dspring.security.oauth2.client.provider.spring.user-name-attribute=sub + + -Dapplication.frontend.error-oauth2-page="/error" + -Dapplication.resource-server="https://${DNS_RESOURCE_SERVER}/" + + user: "1005" + mem_limit: 1G + logging: + options: + max-size: "100m" + max-file: "1" + volumes: + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + - type: bind + source: ca-certificates + target: /bindings/ca-certificates + read_only: true + + healthcheck: + test: "/cnb/process/health-check" + interval: 20s + timeout: 5s + retries: 5 + start_period: 40s + networks: + - app-network + extra_hosts: + - "${DNS_AUTHORIZATION_SERVER}:${IP_AUTHORIZATION_SERVER}" + - "${DNS_RESOURCE_SERVER}:${IP_RESOURCE_SERVER}" + + ory-hydra-oauth2-example-client-readonly-frontend: + image: chistousov/ory-hydra-oauth2-example-client-readonly-frontend:1.0.0 + container_name: ory-hydra-oauth2-example-client-readonly-frontend + environment: + # health check + - THC_PATH=/health + - THC_PORT=4000 + + user: "1005" + mem_limit: 512M + logging: + options: + max-size: "100m" + max-file: "1" + volumes: + + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + healthcheck: + test: "/cnb/process/health-check" + interval: 20s + timeout: 5s + retries: 5 + start_period: 40s + depends_on: + ory-hydra-oauth2-example-client-readonly-backend: + condition: service_healthy + networks: + - app-network + + ory-hydra-oauth2-example-client-readonly-gateway-nginx: + image: nginx:1.25.3-bookworm + container_name: ory-hydra-oauth2-example-client-readonly-gateway-nginx + ports: + - 443:443 + healthcheck: + test: '[ -e /var/run/nginx.pid ] || exit 1' + interval: 20s + timeout: 5s + retries: 5 + start_period: 40s + volumes: + - type: bind + source: nginx/nginx.conf + target: /etc/nginx/nginx.conf + read_only: true + + - type: bind + source: nginx/confs + target: /etc/nginx/conf.d + read_only: true + + - type: bind + source: nginx/cert + target: /cert + read_only: true + + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + + restart: unless-stopped + depends_on: + ory-hydra-oauth2-example-client-readonly-backend: + condition: service_healthy + ory-hydra-oauth2-example-client-readonly-frontend: + condition: service_healthy + logging: + options: + max-size: "100m" + max-file: "1" + mem_limit: 1G + networks: + - app-network + + +networks: + app-network: diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/nginx/confs/client-readonly.conf.templ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/nginx/confs/client-readonly.conf.templ new file mode 100644 index 0000000..3c38585 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/nginx/confs/client-readonly.conf.templ @@ -0,0 +1,67 @@ +server { + listen 443 ssl; + http2 on; + server_name ${DNS_CLIENT_READONLY}; + + ssl_certificate /cert/${DNS_CLIENT_READONLY}/${DNS_CLIENT_READONLY}.crt; + ssl_certificate_key /cert/${DNS_CLIENT_READONLY}/${DNS_CLIENT_READONLY}.key; + + add_header Strict-Transport-Security "max-age=31536000; preload" always; + add_header X-Content-Type-Options "nosniff"; + add_header X-Frame-Options "DENY"; + add_header X-XSS-Protection "1; mode=block"; + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + # Docker DNS + resolver 127.0.0.11; + + location ~ ^/(oauth2)/? { + set $ory_hydra_oauth2_example_client_readonly_backend2 http://ory-hydra-oauth2-example-client-readonly-backend:8080; + rewrite /(.*) /$1 break; + proxy_pass $ory_hydra_oauth2_example_client_readonly_backend2; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto-Version $http2; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /; + } + + location /api/ { + set $ory_hydra_oauth2_example_client_readonly_backend http://ory-hydra-oauth2-example-client-readonly-backend:8080; + rewrite /api/(.*) /$1 break; + proxy_pass $ory_hydra_oauth2_example_client_readonly_backend; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto-Version $http2; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /api; + } + location / { + set $ory_hydra_oauth2_example_client_readonly_frontend http://ory-hydra-oauth2-example-client-readonly-frontend:4000; + rewrite /(.*) /$1 break; + proxy_pass $ory_hydra_oauth2_example_client_readonly_frontend; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto-Version $http2; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /; + + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/nginx/confs/default.conf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/nginx/confs/default.conf new file mode 100644 index 0000000..86124f6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/nginx/confs/default.conf @@ -0,0 +1,11 @@ +server { + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/nginx/nginx.conf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/nginx/nginx.conf new file mode 100644 index 0000000..2238388 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/nginx/nginx.conf @@ -0,0 +1,37 @@ + +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + variables_hash_bucket_size 512; + + proxy_buffer_size 128k; + proxy_buffers 4 256k; + proxy_busy_buffers_size 256k; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/.editorconfig b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/.editorconfig new file mode 100644 index 0000000..59d9a3a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/.gitignore b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/build.gradle b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/build.gradle new file mode 100644 index 0000000..19df18e --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/build.gradle @@ -0,0 +1,95 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '2.7.13' + id 'io.spring.dependency-management' version '1.0.15.RELEASE' + id 'jacoco' +} + +ext { + httpproxy = "$System.env.HTTP_PROXY" ?: "" + httpsproxy = "$System.env.HTTPS_PROXY" ?: "" + noproxy = "$System.env.NO_PROXY" ?: "" + + repoImage = "$System.env.REPO_IMAGE" ?: "" + projectName = "$System.env.PROJECT_NAME" ?: "" + versionProj = "$System.env.VERSION" ?: "" + + set('testcontainersVersion', "1.18.3") +} + +group = 'com.github.chistousov' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '11' + withSourcesJar() +} + +compileJava.options.encoding = "UTF-8" +compileTestJava.options.encoding = "UTF-8" + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +jacocoTestReport { + dependsOn test + reports { + xml.required = true + } +} + +repositories { + mavenCentral() +} + +bootBuildImage { + imageName = "${repoImage}/${projectName}:${versionProj}" + environment = [ + // "HTTP_PROXY" : httpproxy.toString(), + // "HTTPS_PROXY" : httpsproxy.toString(), + // "NO_PROXY" : noproxy.toString(), + // add health check for docker + "BP_HEALTH_CHECKER_ENABLED": "true" + ] + buildpacks = ["urn:cnb:builder:paketo-buildpacks/java", "gcr.io/paketo-buildpacks/health-checker:latest"] +} + + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-actuator' + + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' + + implementation 'org.springframework.boot:spring-boot-starter-security' + + implementation 'org.springframework.boot:spring-boot-starter-webflux' + + implementation 'org.springframework.session:spring-session-core' + + compileOnly 'org.projectlombok:lombok' + + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' + annotationProcessor 'org.projectlombok:lombok' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'io.projectreactor:reactor-test' + testImplementation 'org.springframework.security:spring-security-test' + testImplementation 'org.testcontainers:junit-jupiter' + + testImplementation "org.testcontainers:mockserver:1.18.3" + testImplementation "org.mock-server:mockserver-client-java:5.15.0" +} + +dependencyManagement { + imports { + mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}" + } +} + +tasks.named('test') { + useJUnitPlatform() + finalizedBy jacocoTestReport +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/build_image.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/build_image.bash new file mode 100644 index 0000000..93f1c75 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/build_image.bash @@ -0,0 +1,20 @@ +#!/bin/bash + +# export HTTP_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +# export HTTPS_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +# export NO_PROXY="localhost,127.0.0.1" + +export REPO_IMAGE="chistousov" +export PROJECT_NAME="ory-hydra-oauth2-example-client-readonly-backend" +export VERSION="1.0.0" + +docker pull paketobuildpacks/builder-jammy-full:0.3.316 + +./gradlew clean test +./gradlew bootBuildImage --builder=paketobuildpacks/builder-jammy-full:0.3.316 + +# publish in docker hub +# docker login +# docker push $REPO_IMAGE/$PROJECT_NAME:$VERSION +# docker logout +# docker rmi $REPO_IMAGE/$PROJECT_NAME:$VERSION diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/gradle/wrapper/gradle-wrapper.jar b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..c1962a7 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/gradle/wrapper/gradle-wrapper.jar differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/gradle/wrapper/gradle-wrapper.properties b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..37aef8d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/gradlew b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/gradlew new file mode 100644 index 0000000..aeb74cb --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/gradlew @@ -0,0 +1,245 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/gradlew.bat b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/lombok.config b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/lombok.config new file mode 100644 index 0000000..8f7e8aa --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/lombok.config @@ -0,0 +1 @@ +lombok.addLombokGeneratedAnnotation = true \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/settings.gradle b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/settings.gradle new file mode 100644 index 0000000..6a07f52 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'readonly-backend' diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/ReadonlyBackendApplication.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/ReadonlyBackendApplication.java new file mode 100644 index 0000000..71fe543 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/ReadonlyBackendApplication.java @@ -0,0 +1,16 @@ +package com.github.chistousov.readonly_backend; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.github.chistousov.readonly_backend.jacoco_ignore.ExcludeFromJacocoGeneratedReport; + +@SpringBootApplication +@ExcludeFromJacocoGeneratedReport +public class ReadonlyBackendApplication { + + public static void main(String[] args) { + SpringApplication.run(ReadonlyBackendApplication.class, args); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/SpringSecurityConfiguration.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/SpringSecurityConfiguration.java new file mode 100644 index 0000000..db08c69 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/SpringSecurityConfiguration.java @@ -0,0 +1,94 @@ +package com.github.chistousov.readonly_backend; + +import java.util.Arrays; +import java.util.stream.Collectors; + +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.SecurityWebFiltersOrder; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; +import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestCustomizers; +import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction; +import org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver; +import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver; +import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository; +import org.springframework.security.web.server.SecurityWebFilterChain; +import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository; +import org.springframework.web.reactive.function.client.WebClient; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.github.chistousov.readonly_backend.security.auth.logout.success_handlers.CustomServerLogoutSuccessHandler; +import com.github.chistousov.readonly_backend.security.auth.oauth2.handlers.CustomServerAuthenticationFailureHandler; +import com.github.chistousov.readonly_backend.security.filters.AuthenticateFrontendWebFilter; +import com.github.chistousov.readonly_backend.security.filters.IsAuthenticatedCheckWebFilter; + +@EnableWebFluxSecurity +public class SpringSecurityConfiguration { + + private String pathFrontendErrorPage; + private String pathReauthenticationLocation; + + public SpringSecurityConfiguration(Environment env) { + + String clientId = env.getRequiredProperty( + "spring.security.oauth2.client.registration.client-readonly.client-id"); + + this.pathReauthenticationLocation = Arrays.asList( + "oauth2", + "authorization", + clientId) + .stream() + .collect(Collectors.joining("/")); + + this.pathFrontendErrorPage = env.getRequiredProperty( + "application.frontend.error-oauth2-page"); + + } + + @Bean + public SecurityWebFilterChain pkceFilterChain(ServerHttpSecurity http, + ServerOAuth2AuthorizationRequestResolver resolver) throws JsonProcessingException { + return http.authorizeExchange( + ex -> ex.pathMatchers("/actuator/health") + .permitAll() + // все запросы должны быть аутентифицированы + .anyExchange() + .authenticated() + + ) + // CSRF для SPA приложений + .csrf(csrf -> csrf.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())) + .oauth2Login() + .authorizationRequestResolver(resolver) + .authenticationFailureHandler( + new CustomServerAuthenticationFailureHandler(pathFrontendErrorPage, pathReauthenticationLocation)) + .and() + // конфигурация logout + .logout() + .logoutSuccessHandler(new CustomServerLogoutSuccessHandler()) + .and() + .addFilterBefore(new IsAuthenticatedCheckWebFilter(), SecurityWebFiltersOrder.HTTP_BASIC) + .addFilterBefore(new AuthenticateFrontendWebFilter(), SecurityWebFiltersOrder.HTTP_BASIC) + .build(); + } + + @Bean + public ServerOAuth2AuthorizationRequestResolver pkceResolver(ReactiveClientRegistrationRepository repo) { + var resolver = new DefaultServerOAuth2AuthorizationRequestResolver(repo); + resolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce()); + return resolver; + } + + @Bean + WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations, + ServerOAuth2AuthorizedClientRepository authorizedClients) { + ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction( + clientRegistrations, + authorizedClients); + return WebClient.builder() + .filter(oauth) + .build(); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/controllers/DataController.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/controllers/DataController.java new file mode 100644 index 0000000..c25ef90 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/controllers/DataController.java @@ -0,0 +1,57 @@ +package com.github.chistousov.readonly_backend.controllers; + +import java.net.URI; + +import org.springframework.core.env.Environment; +import org.springframework.http.ResponseEntity; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.server.WebSession; +import org.springframework.web.util.UriComponentsBuilder; + +import reactor.core.publisher.Mono; + +@Controller +@RequestMapping("/data") +public class DataController { + + private WebClient webClient; + private URI locationResourceServer; + + private URI locationStatistics; + + public DataController(WebClient webClient, Environment env) { + this.webClient = webClient; + this.locationResourceServer = URI.create( + env.getRequiredProperty( + "application.resource-server")); + + this.locationStatistics = UriComponentsBuilder + .fromUri(locationResourceServer) + .pathSegment("api", "statistics") + .build() + .toUri(); + } + + @GetMapping() + public Mono> getStatistics( + @RegisteredOAuth2AuthorizedClient("client-readonly") OAuth2AuthorizedClient authorizedClient, + WebSession webSession) { + + return Mono.just( + ResponseEntity.ok(webClient.get() + .uri(locationStatistics) + .attributes( + ServerOAuth2AuthorizedClientExchangeFilterFunction + .oauth2AuthorizedClient( + authorizedClient)) + .retrieve() + .bodyToMono(Object.class))); + + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/controllers/GlobalExceptionHandler.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/controllers/GlobalExceptionHandler.java new file mode 100644 index 0000000..1f5b7d6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/controllers/GlobalExceptionHandler.java @@ -0,0 +1,71 @@ +package com.github.chistousov.readonly_backend.controllers; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.support.WebExchangeBindException; + +import com.github.chistousov.readonly_backend.jacoco_ignore.ExcludeFromJacocoGeneratedReport; + +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@ControllerAdvice +@Slf4j +@ExcludeFromJacocoGeneratedReport +public class GlobalExceptionHandler { + + private static final String TEXT_PLAIN_CHARSET_UTF_8 = "text/plain;charset=utf-8"; + private static final String CONTENT_TYPE = "Content-Type"; + + // invalid model handler + @ExceptionHandler(WebExchangeBindException.class) + public ResponseEntity> handleException(WebExchangeBindException e) { + var errors = e.getBindingResult() + .getAllErrors() + .stream() + .map(DefaultMessageSourceResolvable::getDefaultMessage) + .collect(Collectors.toList()); + return ResponseEntity.badRequest().body(errors); + + } + + @ExceptionHandler(IllegalArgumentException.class) + public Mono illegalArgumentPerfom(ServerHttpRequest req, ServerHttpResponse response, + IllegalArgumentException ex) { + + response.getHeaders().set(CONTENT_TYPE, TEXT_PLAIN_CHARSET_UTF_8); + response.setStatusCode(HttpStatus.BAD_REQUEST); + + byte[] bodyResponse = "Request received with incorrect data ".getBytes(StandardCharsets.UTF_8); + DataBuffer buffer = response.bufferFactory().wrap(bodyResponse); + + return response.writeWith(Flux.just(buffer)) + .doOnEach(el -> log.error(req.getPath().toString() + " ", ex)); + + } + + @ExceptionHandler(Exception.class) + public Mono commonHandler(ServerHttpRequest req, ServerHttpResponse response, Exception ex) { + + response.getHeaders().set(CONTENT_TYPE, TEXT_PLAIN_CHARSET_UTF_8); + response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + + byte[] bodyResponse = "Error in server ".getBytes(StandardCharsets.UTF_8); + DataBuffer buffer = response.bufferFactory().wrap(bodyResponse); + + return response.writeWith(Flux.just(buffer)) + .doOnEach(el -> log.error(req.getPath().toString() + " ", ex)); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/jacoco_ignore/ExcludeFromJacocoGeneratedReport.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/jacoco_ignore/ExcludeFromJacocoGeneratedReport.java new file mode 100644 index 0000000..9c35691 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/jacoco_ignore/ExcludeFromJacocoGeneratedReport.java @@ -0,0 +1,21 @@ +package com.github.chistousov.readonly_backend.jacoco_ignore; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marking with this annotation, we ignore code coverage by JaCoCo tests for a + * class or method. + * Помечая данной аннотацией, мы игнорируем для класса или метода покрытие кода + * тестами JaCoCo + */ +@Documented +@Retention(RUNTIME) +@Target({ TYPE, METHOD }) +public @interface ExcludeFromJacocoGeneratedReport { +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/security/auth/logout/success_handlers/CustomServerLogoutSuccessHandler.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/security/auth/logout/success_handlers/CustomServerLogoutSuccessHandler.java new file mode 100644 index 0000000..a750684 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/security/auth/logout/success_handlers/CustomServerLogoutSuccessHandler.java @@ -0,0 +1,23 @@ +package com.github.chistousov.readonly_backend.security.auth.logout.success_handlers; + +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.server.WebFilterExchange; +import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler; + +import reactor.core.publisher.Mono; + +public class CustomServerLogoutSuccessHandler implements ServerLogoutSuccessHandler { + + private static final HttpStatus httpStatus = HttpStatus.NO_CONTENT; + + @Override + public Mono onLogoutSuccess(WebFilterExchange webFilterExchange, Authentication authentication) { + return Mono.fromRunnable(() -> { + ServerHttpResponse response = webFilterExchange.getExchange().getResponse(); + response.setStatusCode(httpStatus); + }); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/security/auth/oauth2/handlers/CustomServerAuthenticationFailureHandler.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/security/auth/oauth2/handlers/CustomServerAuthenticationFailureHandler.java new file mode 100644 index 0000000..0577570 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/security/auth/oauth2/handlers/CustomServerAuthenticationFailureHandler.java @@ -0,0 +1,81 @@ +package com.github.chistousov.readonly_backend.security.auth.oauth2.handlers; + +import java.net.URI; +import java.util.List; + +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.server.WebFilterExchange; +import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler; +import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.util.UriComponentsBuilder; + +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; + +@Slf4j +public class CustomServerAuthenticationFailureHandler implements ServerAuthenticationFailureHandler { + + private static final HttpStatus httpStatus = HttpStatus.FOUND; + + private static final String ERROR = "error"; + private static final String ERROR_DESCRIPTION = "error_description"; + private static final String REAUTHENTICATION_LOCATION = "reauthentication_location"; + + private static final String DEFAULT_ERROR = "server_error"; + private static final String DEFAULT_ERROR_DESCRIPTION = "Неизвестная ошибка сервера"; + + private URI location; + private String reauthenticationLocation; + + public CustomServerAuthenticationFailureHandler(String location, String reauthenticationLocation) { + Assert.notNull(location, "location cannot be null"); + Assert.notNull(reauthenticationLocation, "reauthenticationLocation cannot be null"); + this.location = URI.create(location); + this.reauthenticationLocation = reauthenticationLocation; + } + + @Override + public Mono onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception) { + + // что оправиться с редиректом + String queryParamError = null; + String queryParamErrorDescription = null; + + // достаем необходимые параметры запроса + MultiValueMap queryParamsRequest = webFilterExchange.getExchange().getRequest().getQueryParams(); + var queryParamsError = queryParamsRequest.getOrDefault(ERROR, List.of(DEFAULT_ERROR)); + var queryParamsErrorDescription = queryParamsRequest.getOrDefault(ERROR_DESCRIPTION, + List.of(DEFAULT_ERROR_DESCRIPTION)); + + queryParamError = queryParamsError.get(0); + + queryParamErrorDescription = queryParamsErrorDescription.get(0); + + // формируем необходимые параметры запроса для редиректа + MultiValueMap queryParamsForRedirect = new LinkedMultiValueMap<>(); + queryParamsForRedirect.add(ERROR, queryParamError); + queryParamsForRedirect.add(ERROR_DESCRIPTION, queryParamErrorDescription); + queryParamsForRedirect.add(REAUTHENTICATION_LOCATION, reauthenticationLocation); + + URI redirectToFrontendLocation = UriComponentsBuilder + .fromUri(location) + .queryParams(queryParamsForRedirect) + .build() + .toUri(); + + return Mono.fromRunnable(() -> { + ServerHttpResponse response = webFilterExchange.getExchange().getResponse(); + response.setStatusCode(httpStatus); + + log.debug("Redirecting to '{}'", redirectToFrontendLocation.toASCIIString()); + + response.getHeaders().setLocation(redirectToFrontendLocation); + + }); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/security/filters/AuthenticateFrontendWebFilter.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/security/filters/AuthenticateFrontendWebFilter.java new file mode 100644 index 0000000..25a57c2 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/security/filters/AuthenticateFrontendWebFilter.java @@ -0,0 +1,57 @@ +package com.github.chistousov.readonly_backend.security.filters; + +import java.net.URI; + +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; + +import reactor.core.publisher.Mono; + +public class AuthenticateFrontendWebFilter implements WebFilter { + + private static final String AUTHENTICATION_FRONTEND = "/authenticate-frontend"; + + private ServerWebExchangeMatcher requiresAuthenticateFrontendMatcher = ServerWebExchangeMatchers + .pathMatchers(HttpMethod.GET, AUTHENTICATION_FRONTEND); + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + // endpoint, который если пользователь аутентифицирован всегда возвращает / + + // соответствует URI /authenticate-frontend + return this.requiresAuthenticateFrontendMatcher.matches(exchange) + .filter(MatchResult::isMatch) + // request не соответствует /authenticate-frontend, тогда пропускаем + .switchIfEmpty(chain.filter(exchange).then(Mono.empty())) + // есть ли контекст безопасности? + .flatMap(matchResult -> ReactiveSecurityContextHolder + .getContext() + // нету констекста безопасности значит создаем фиктивный объект + .switchIfEmpty(Mono.just(new SecurityContextImpl()))) + .flatMap(securityContext -> { + Authentication authentication = securityContext.getAuthentication(); + // если контекст безопасности и пользователь аутентифицирован + if (authentication != null && authentication.isAuthenticated()) { + + return Mono.fromRunnable(() -> { + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(HttpStatus.FOUND); + + response.getHeaders().setLocation(URI.create("/")); + }); + } + // иначе обрабатываем дальше по фильтрам запрос + return chain.filter(exchange); + }); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/security/filters/IsAuthenticatedCheckWebFilter.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/security/filters/IsAuthenticatedCheckWebFilter.java new file mode 100644 index 0000000..0c60aff --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/java/com/github/chistousov/readonly_backend/security/filters/IsAuthenticatedCheckWebFilter.java @@ -0,0 +1,85 @@ +package com.github.chistousov.readonly_backend.security.filters; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import reactor.core.publisher.Mono; + +public class IsAuthenticatedCheckWebFilter implements WebFilter { + + private static final String AUTHENTICATION_FRONTEND = "/authenticate-frontend"; + + private ServerWebExchangeMatcher requiresLoggedInMatcher = ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, + "/logged-in"); + + private String json; + + public IsAuthenticatedCheckWebFilter() throws JsonProcessingException { + Map payload = new HashMap<>(); + payload.put("redirect_to", AUTHENTICATION_FRONTEND); + + json = new ObjectMapper().writeValueAsString(payload); + } + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + // проверяем есть аутентификация и авторизация + + // соответствует URI /logged-in + return this.requiresLoggedInMatcher.matches(exchange) + .filter(MatchResult::isMatch) + // request не соответствует /logged-in, тогда пропускаем + .switchIfEmpty(chain.filter(exchange).then(Mono.empty())) + // есть ли контекст безопасности? + .flatMap(matchResult -> ReactiveSecurityContextHolder + .getContext() + // нету констекста безопасности значит создаем фиктивный объект + .switchIfEmpty(Mono.just(new SecurityContextImpl()))) + .flatMap(securityContext -> { + Authentication authentication = securityContext.getAuthentication(); + // нету констекста безопасности или не аутентифицирован + if (authentication == null || !authentication.isAuthenticated()) { + + return Mono.defer(() -> { + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(HttpStatus.UNAUTHORIZED); + response.getHeaders().setContentType(MediaType.TEXT_PLAIN); + + DataBuffer dataBuffer = response.bufferFactory() + .wrap(json.getBytes(StandardCharsets.UTF_8)); + + return response.writeAndFlushWith(Mono.just(Mono.just(dataBuffer))) + .doOnError(ex -> DataBufferUtils.release(dataBuffer)); + }); + } + + // иначе 204 - все хорошо! + return Mono.fromRunnable(() -> { + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(HttpStatus.NO_CONTENT); + response.getHeaders().setContentType(MediaType.TEXT_PLAIN); + }); + }); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000..ee8583f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,5 @@ +{"properties": [{ + "name": "application.title", + "type": "java.lang.String", + "description": "title this app" +}]} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/resources/application-prod.yml b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/resources/application-prod.yml new file mode 100644 index 0000000..c56eee6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/resources/application-prod.yml @@ -0,0 +1,3 @@ +logging: + level: + root: info \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/resources/application.yml b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/resources/application.yml new file mode 100644 index 0000000..8da6d0a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/resources/application.yml @@ -0,0 +1,32 @@ +application: + title: OAuth 2.0 Client Readonly Example Ory Hydra + +spring: + + output: + ansi: + enabled: always + + security: + oauth2: + client: + registration: + client-readonly: + provider: spring + client-authentication-method: client_secret_basic + authorization-grant-type: authorization_code + +logging: + level: + root: debug + + +server: + forward-headers-strategy: framework + reactive: + session: + cookie: + name: SESSION-CLIENT-READONLY + error: + include-stacktrace: never + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/resources/banner.txt b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/resources/banner.txt new file mode 100644 index 0000000..a4e1230 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/main/resources/banner.txt @@ -0,0 +1,7 @@ +${AnsiColor.BRIGHT_CYAN} +,---.| o | +| |---..,---.|--- ,---.. .,---.,---.. , +| | ||`---.| | || |`---.| | \ / +`---'` '``---'`---'`---'`---'`---'`---' `' +${AnsiBackground.WHITE}${AnsiColor.BRIGHT_BLACK}${application.title} +Powered by Spring Boot ${spring-boot.version}${AnsiColor.DEFAULT}${AnsiBackground.DEFAULT} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/test/java/com/github/chistousov/readonly_backend/controllers/DataControllerTest.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/test/java/com/github/chistousov/readonly_backend/controllers/DataControllerTest.java new file mode 100644 index 0000000..fe0e990 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/test/java/com/github/chistousov/readonly_backend/controllers/DataControllerTest.java @@ -0,0 +1,579 @@ +package com.github.chistousov.readonly_backend.controllers; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Random; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockserver.client.MockServerClient; +import org.mockserver.file.FileReader; +import org.mockserver.model.Header; +import org.mockserver.model.OpenAPIDefinition; +import org.mockserver.verify.VerificationTimes; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.context.annotation.Import; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.util.UriComponentsBuilder; +import org.testcontainers.containers.MockServerContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.shaded.com.google.common.collect.ImmutableMap; +import org.testcontainers.utility.DockerImageName; + +import com.github.chistousov.readonly_backend.SpringSecurityConfiguration; + +import static org.mockserver.mock.OpenAPIExpectation.openAPIExpectation; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; +import static org.mockserver.model.JsonBody.json; + +@WebFluxTest +@Import(SpringSecurityConfiguration.class) +@Testcontainers +public class DataControllerTest { + + private static final String SESSION_CLIENT_READONLY = "SESSION"; + + private static final DockerImageName MOCKSERVER_IMAGE = DockerImageName + .parse("mockserver/mockserver") + .withTag("mockserver-" + MockServerClient.class.getPackage().getImplementationVersion()); + + @Container + public static MockServerContainer oryHydraMockServer = new MockServerContainer(MOCKSERVER_IMAGE); + private static MockServerClient oryHydraMockServerClient; + + private static final String clientId = "someUser"; + private static final String clientSecret = "someSecret"; + + private static String authorizationUri; + private static String redirectUri; + + @Container + public static MockServerContainer resourceServerMockServer = new MockServerContainer(MOCKSERVER_IMAGE); + private static MockServerClient resourceServerMockServerClient; + + @Autowired + private WebTestClient thisServerWebTestClient; + + private static WebTestClient oryHydraWebTestClient; + + @DynamicPropertySource + public static void settings(DynamicPropertyRegistry registry) + throws UnsupportedEncodingException { + + String baseHostPath = "http://localhost:8080"; + + oryHydraMockServerClient = new MockServerClient(oryHydraMockServer.getHost(), + oryHydraMockServer.getServerPort()); + + String baseOryHydraPath = String.format("http://%s:%d", oryHydraMockServer.getHost(), + oryHydraMockServer.getServerPort()); + + resourceServerMockServerClient = new MockServerClient(resourceServerMockServer.getHost(), + resourceServerMockServer.getServerPort()); + + String baseResourceServerPath = String.format("http://%s:%d", resourceServerMockServer.getHost(), + resourceServerMockServer.getServerPort()); + + registry.add("spring.security.oauth2.client.registration.client-readonly.client-id", () -> clientId); + registry.add("spring.security.oauth2.client.registration.client-readonly.client-secret", () -> clientSecret); + + redirectUri = UriComponentsBuilder + .fromUri(URI.create(baseHostPath)) + .pathSegment("login", "oauth2", "code", "client-readonly") + .build() + .toUriString(); + + registry.add("spring.security.oauth2.client.registration.client-readonly.redirect-uri", () -> redirectUri); + + authorizationUri = UriComponentsBuilder + .fromUri(URI.create(baseOryHydraPath)) + .pathSegment("oauth2", "auth") + .build() + .toUriString(); + + oryHydraWebTestClient = WebTestClient.bindToServer() + .baseUrl(baseOryHydraPath) + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .defaultHeader("X-Forwarded-Proto", "https") + .build(); + + registry.add("spring.security.oauth2.client.provider.spring.authorization-uri", () -> authorizationUri); + registry.add("spring.security.oauth2.client.provider.spring.token-uri", () -> UriComponentsBuilder + .fromUri(URI.create(baseOryHydraPath)) + .pathSegment("oauth2", "token") + .build() + .toUriString()); + registry.add("spring.security.oauth2.client.provider.spring.user-info-uri", () -> UriComponentsBuilder + .fromUri(URI.create(baseOryHydraPath)) + .pathSegment("userinfo") + .build() + .toUriString()); + registry.add("spring.security.oauth2.client.provider.spring.user-name-attribute", () -> "sub"); + + registry.add("application.frontend.error-oauth2-page", () -> "/error"); + + registry.add("application.resource-server", () -> baseResourceServerPath); + + registry.add("server.reactive.session.cookie.name", () -> SESSION_CLIENT_READONLY); + } + + @BeforeEach + void setUp() { + oryHydraMockServerClient.reset(); + resourceServerMockServerClient.reset(); + + thisServerWebTestClient = thisServerWebTestClient.mutate() + .responseTimeout(Duration.ofMinutes(5)) + .build(); + } + + String authLocation; + String cookieThisSession; + + @Test + @DisplayName("/data is success") + void testGetStatistics() { + // given (instead of when) + + oryHydraMockServerClient + .upsert( + openAPIExpectation( + FileReader.readFileFromClassPathOrPath( + "src/test/resources/ory-hydra-open-api-v3.json"), + ImmutableMap.of( + "oAuth2Authorize", "302", + "getOidcUserInfo", "200"))); + + oryHydraMockServerClient + .when( + new OpenAPIDefinition() + .withSpecUrlOrPayload( + FileReader.readFileFromClassPathOrPath( + "src/test/resources/ory-hydra-open-api-v3.json")) + .withOperationId("oauth2TokenExchange")) + .respond( + response() + .withStatusCode(200) + .withHeaders(new Header("Content-Type", "application/json; charset=utf-8")) + .withBody( + json( + "{\n" + + "\"access_token\": \"gAanWA6Hym67rOq5k0ZGUWp3dR_fxILn3cyRj4hPt-c.wVdASIZBWL1XjHTz-3UImRkpTJLMbFwCMkPeOIh_sSM\",\n" + + + "\"expires_in\": 8888,\n" + + "\"id_token\": 68678,\n" + + "\"refresh_token\": \"gAanWA6Hym67rOq5k0ZGUWp3dR_fxILn3cyRj4hPt-c.wVdASIZBWL1XjHTz-3UImRkpTJLMbFwCMkPeOIh_sSM\",\n" + + + "\"scope\": \"read\",\n" + + "\"token_type\": \"bearer\"\n" + + "}"))); + + resourceServerMockServerClient + .upsert( + openAPIExpectation( + FileReader.readFileFromClassPathOrPath( + "src/test/resources/resource-server-open-api-v3.json"), + ImmutableMap.of( + "getStatistics", "200" + + ))); + + // when + + // проверка аутентифицирован(и авторизован) ли пользователь без сессии + thisServerWebTestClient + .get() + .uri("/logged-in") + .exchange() + .expectStatus() + .isUnauthorized() + .expectBody() + .jsonPath("$").isNotEmpty() + .jsonPath("$").isMap() + .jsonPath("$.length()").isEqualTo(1) + .jsonPath("$.redirect_to").isNotEmpty() + .jsonPath("$.redirect_to").isEqualTo("/authenticate-frontend"); + + // аутентифицируемся + thisServerWebTestClient + .get() + .uri("/authenticate-frontend") + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", "/oauth2/authorization/client-readonly"); + + thisServerWebTestClient + .get() + .uri("/oauth2/authorization/client-readonly") + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", String.format( + "^http:\\/\\/%s:%d\\/oauth2\\/auth\\?response_type=code&client_id=%s&state=.+&redirect_uri=%s&code_challenge=.+&code_challenge_method=S256$", + oryHydraMockServer.getHost(), + oryHydraMockServer.getServerPort(), + clientId, + redirectUri)) + .expectHeader() + .value("Location", location -> authLocation = location) + .expectCookie() + .exists(SESSION_CLIENT_READONLY) + .expectCookie() + .value(SESSION_CLIENT_READONLY, val -> cookieThisSession = val); + + URI authLocationURI = URI.create(authLocation); + var queryParamsStrAuthLocationURI = authLocationURI.getQuery(); + + var queryParamsAuthLocationURI = Arrays + .stream(queryParamsStrAuthLocationURI.split("&")) + .map(this::splitQueryParameter) + .collect( + Collectors.groupingBy( + SimpleImmutableEntry::getKey, + LinkedHashMap::new, + Collectors.mapping(Map.Entry::getValue, + Collectors.toList()))); + + String state = queryParamsAuthLocationURI.get("state").get(0); + + oryHydraWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/oauth2/auth") + .query(queryParamsStrAuthLocationURI) + .build()) + .exchange() + .expectStatus() + .isFound(); + + // прошла аутентификация и авторизация ... Получаем code в redirect uri. + + String code = "someCode"; + + thisServerWebTestClient + .get() + .uri( + + uriBuilder -> uriBuilder + .path("/login/oauth2/code/client-readonly") + .queryParam("code", code) + .queryParam("state", state) + .build()) + .cookie(SESSION_CLIENT_READONLY, cookieThisSession) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", "/") + .expectCookie() + .exists(SESSION_CLIENT_READONLY) + .expectCookie() + .value(SESSION_CLIENT_READONLY, val -> cookieThisSession = val); + + // Мы аутентифицированы и авторизованы + + // проверим еще раз + + thisServerWebTestClient + .get() + .uri("/logged-in") + .cookie(SESSION_CLIENT_READONLY, cookieThisSession) + .exchange() + .expectStatus() + .isNoContent(); + + thisServerWebTestClient + .get() + .uri("/authenticate-frontend") + .cookie(SESSION_CLIENT_READONLY, cookieThisSession) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", "/"); + + // получем данные с сессией (аутентифицирован и авторизован) + + thisServerWebTestClient + .get() + .uri( + + uriBuilder -> uriBuilder + .path("/data") + .build()) + .cookie(SESSION_CLIENT_READONLY, cookieThisSession) + .exchange() + .expectStatus() + .isOk() + .expectBody() + .jsonPath("$").isNotEmpty() + .jsonPath("$").isArray() + .jsonPath("$.length()").isEqualTo(1); + + // выход + + String tokenCSRF = generateStr(); + + thisServerWebTestClient + .post() + .uri( + + uriBuilder -> uriBuilder + .path("/logout") + .build()) + .cookie("XSRF-TOKEN", tokenCSRF) + .header("X-XSRF-TOKEN", tokenCSRF) + .cookie(SESSION_CLIENT_READONLY, cookieThisSession) + .exchange() + .expectStatus() + .isNoContent(); + + // then (instead of verify + + oryHydraMockServerClient.verify( + request() + .withMethod("GET") + .withPath("/oauth2/auth"), + VerificationTimes.exactly(1)); + + oryHydraMockServerClient.verify( + request() + .withMethod("POST") + .withPath("/oauth2/token"), + VerificationTimes.exactly(1)); + + oryHydraMockServerClient.verify( + request() + .withMethod("GET") + .withPath("/userinfo"), + VerificationTimes.exactly(1)); + + resourceServerMockServerClient.verify( + request() + .withMethod("GET") + .withPath("/api/statistics"), + VerificationTimes.exactly(1)); + + } + + @Test + @DisplayName("/data is fauil. Token is incorrect") + void testGetStatisticsTokenIncorrect() { + // given (instead of when) + + oryHydraMockServerClient + .upsert( + openAPIExpectation( + FileReader.readFileFromClassPathOrPath( + "src/test/resources/ory-hydra-open-api-v3.json"), + ImmutableMap.of( + "oAuth2Authorize", "302", + "oauth2TokenExchange", "200"))); + + // when + + // проверка аутентифицирован(и авторизован) ли пользователь без сессии + thisServerWebTestClient + .get() + .uri("/logged-in") + .exchange() + .expectStatus() + .isUnauthorized() + .expectBody() + .jsonPath("$").isNotEmpty() + .jsonPath("$").isMap() + .jsonPath("$.length()").isEqualTo(1) + .jsonPath("$.redirect_to").isNotEmpty() + .jsonPath("$.redirect_to").isEqualTo("/authenticate-frontend"); + + // аутентифицируемся + thisServerWebTestClient + .get() + .uri("/authenticate-frontend") + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", "/oauth2/authorization/client-readonly"); + + thisServerWebTestClient + .get() + .uri("/oauth2/authorization/client-readonly") + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", String.format( + "^http:\\/\\/%s:%d\\/oauth2\\/auth\\?response_type=code&client_id=%s&state=.+&redirect_uri=%s&code_challenge=.+&code_challenge_method=S256$", + oryHydraMockServer.getHost(), + oryHydraMockServer.getServerPort(), + clientId, + redirectUri)) + .expectHeader() + .value("Location", location -> authLocation = location) + .expectCookie() + .exists(SESSION_CLIENT_READONLY) + .expectCookie() + .value(SESSION_CLIENT_READONLY, val -> cookieThisSession = val); + + URI authLocationURI = URI.create(authLocation); + var queryParamsStrAuthLocationURI = authLocationURI.getQuery(); + + var queryParamsAuthLocationURI = Arrays + .stream(queryParamsStrAuthLocationURI.split("&")) + .map(this::splitQueryParameter) + .collect( + Collectors.groupingBy( + SimpleImmutableEntry::getKey, + LinkedHashMap::new, + Collectors.mapping(Map.Entry::getValue, + Collectors.toList()))); + + String state = queryParamsAuthLocationURI.get("state").get(0); + + oryHydraWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/oauth2/auth") + .query(queryParamsStrAuthLocationURI) + .build()) + .exchange() + .expectStatus() + .isFound(); + + // прошла аутентификация и авторизация ... Получаем code в redirect uri. + + String code = "someCode"; + + thisServerWebTestClient + .get() + .uri( + + uriBuilder -> uriBuilder + .path("/login/oauth2/code/client-readonly") + .queryParam("code", code) + .queryParam("state", state) + .build()) + .cookie(SESSION_CLIENT_READONLY, cookieThisSession) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", String.format( + "^\\/error\\?error=%s&error_description=%s&reauthentication_location=%s$", + "server_error", + URLEncoder.encode("Неизвестная ошибка сервера", StandardCharsets.UTF_8).replace("+", "%20"), + "oauth2\\/authorization\\/someUser")); + + // then (instead of verify + + oryHydraMockServerClient.verify( + request() + .withMethod("GET") + .withPath("/oauth2/auth"), + VerificationTimes.exactly(1)); + + oryHydraMockServerClient.verify( + request() + .withMethod("POST") + .withPath("/oauth2/token"), + VerificationTimes.exactly(1)); + + } + + @Test + @DisplayName("/oauth2/auth is faul.") + void fauilRequestToOryHydra() { + // given (instead of when) + + final String error = "invalid_request"; + final String errorDescription = "Очень серьезная ошибка"; + + // when + + thisServerWebTestClient + .get() + .uri( + + uriBuilder -> uriBuilder + .path("/login/oauth2/code/client-readonly") + .queryParam("error", error) + .queryParam("error_description", errorDescription) + .queryParam("state", "xyz") + .build()) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", String.format( + "^\\/error\\?error=%s&error_description=%s&reauthentication_location=%s$", + error, + URLEncoder.encode(errorDescription, StandardCharsets.UTF_8).replace("+", "%20"), + "oauth2\\/authorization\\/someUser")); + + // then (instead of verify + + } + + // для парсинга QueryParameter URI + private SimpleImmutableEntry splitQueryParameter(String it) { + final int idx = it.indexOf("="); + final String key = idx > 0 ? it.substring(0, idx) : it; + final String value = idx > 0 && it.length() > idx + 1 ? it.substring(idx + 1) : null; + return new SimpleImmutableEntry<>( + URLDecoder.decode(key, StandardCharsets.UTF_8), + URLDecoder.decode(value, StandardCharsets.UTF_8)); + } + + private static String generateStr() { + // генерим случайную строки для state + int leftLimit = 97; // letter 'a' + int rightLimit = 122; // letter 'z' + int targetStringLength = 30; + Random random = new Random(); + StringBuilder buffer = new StringBuilder(targetStringLength); + for (int i = 0; i < targetStringLength; i++) { + int randomLimitedInt = leftLimit + (int) (random.nextFloat() * (rightLimit - leftLimit + 1)); + buffer.append((char) randomLimitedInt); + } + return buffer.toString(); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/test/resources/ory-hydra-open-api-v3.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/test/resources/ory-hydra-open-api-v3.json new file mode 100644 index 0000000..f812412 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/test/resources/ory-hydra-open-api-v3.json @@ -0,0 +1,3689 @@ +{ + "components": { + "responses": { + "emptyResponse": { + "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is\ntypically 201." + }, + "errorOAuth2BadRequest": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "Bad Request Error Response" + }, + "errorOAuth2Default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "Default Error Response" + }, + "errorOAuth2NotFound": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "Not Found Error Response" + }, + "listOAuth2Clients": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/oAuth2Client" + }, + "type": "array" + } + } + }, + "description": "Paginated OAuth2 Client List Response" + } + }, + "schemas": { + "DefaultError": {}, + "JSONRawMessage": { + "title": "JSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger." + }, + "NullBool": { + "nullable": true, + "type": "boolean" + }, + "NullDuration": { + "description": "Specify a time duration in milliseconds, seconds, minutes, hours.", + "pattern": "^([0-9]+(ns|us|ms|s|m|h))*$", + "title": "Time duration", + "type": "string" + }, + "NullInt": { + "nullable": true, + "type": "integer" + }, + "NullString": { + "nullable": true, + "type": "string" + }, + "NullTime": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "NullUUID": { + "format": "uuid4", + "nullable": true, + "type": "string" + }, + "StringSliceJSONFormat": { + "items": { + "type": "string" + }, + "title": "StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.", + "type": "array" + }, + "Time": { + "format": "date-time", + "type": "string" + }, + "UUID": { + "format": "uuid4", + "type": "string" + }, + "acceptOAuth2ConsentRequest": { + "properties": { + "grant_access_token_audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "grant_scope": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "handled_at": { + "$ref": "#/components/schemas/nullTime" + }, + "remember": { + "description": "Remember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same\nclient asks the same user for the same, or a subset of, scope.", + "type": "boolean" + }, + "remember_for": { + "description": "RememberFor sets how long the consent authorization should be remembered for in seconds. If set to `0`, the\nauthorization will be remembered indefinitely.", + "format": "int64", + "type": "integer" + }, + "session": { + "$ref": "#/components/schemas/acceptOAuth2ConsentRequestSession" + } + }, + "title": "The request payload used to accept a consent request.", + "type": "object" + }, + "acceptOAuth2ConsentRequestSession": { + "properties": { + "access_token": { + "description": "AccessToken sets session data for the access and refresh token, as well as any future tokens issued by the\nrefresh grant. Keep in mind that this data will be available to anyone performing OAuth 2.0 Challenge Introspection.\nIf only your services can perform OAuth 2.0 Challenge Introspection, this is usually fine. But if third parties\ncan access that endpoint as well, sensitive data from the session might be exposed to them. Use with care!" + }, + "id_token": { + "description": "IDToken sets session data for the OpenID Connect ID token. Keep in mind that the session'id payloads are readable\nby anyone that has access to the ID Challenge. Use with care!" + } + }, + "title": "Pass session data to a consent request.", + "type": "object" + }, + "acceptOAuth2LoginRequest": { + "properties": { + "acr": { + "description": "ACR sets the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it\nto express that, for example, a user authenticated using two factor authentication.", + "type": "string" + }, + "amr": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "context": { + "$ref": "#/components/schemas/JSONRawMessage" + }, + "extend_session_lifespan": { + "description": "Extend OAuth2 authentication session lifespan\n\nIf set to `true`, the OAuth2 authentication cookie lifespan is extended. This is for example useful if you want the user to be able to use `prompt=none` continuously.\n\nThis value can only be set to `true` if the user has an authentication, which is the case if the `skip` value is `true`.", + "type": "boolean" + }, + "force_subject_identifier": { + "description": "ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the\n(Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID\nConnect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client.\n\nPlease note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the\nsub claim in the OAuth 2.0 Introspection.\n\nPer default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself\nyou can use this field. Please note that setting this field has no effect if `pairwise` is not configured in\nORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's\nconfiguration).\n\nPlease also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies\nthat you have to compute this value on every authentication process (probably depending on the client ID or some\nother unique value).\n\nIf you fail to compute the proper value, then authentication processes which have id_token_hint set might fail.", + "type": "string" + }, + "remember": { + "description": "Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store\na cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she\nwill not be asked to log in again.", + "type": "boolean" + }, + "remember_for": { + "description": "RememberFor sets how long the authentication should be remembered for in seconds. If set to `0`, the\nauthorization will be remembered for the duration of the browser session (using a session cookie).", + "format": "int64", + "type": "integer" + }, + "subject": { + "description": "Subject is the user ID of the end-user that authenticated.", + "type": "string" + } + }, + "required": [ + "subject" + ], + "title": "HandledLoginRequest is the request payload used to accept a login request.", + "type": "object" + }, + "createJsonWebKeySet": { + "description": "Create JSON Web Key Set Request Body", + "properties": { + "alg": { + "description": "JSON Web Key Algorithm\n\nThe algorithm to be used for creating the key. Supports `RS256`, `ES256`, `ES512`, `HS512`, and `HS256`.", + "type": "string" + }, + "kid": { + "description": "JSON Web Key ID\n\nThe Key ID of the key to be created.", + "type": "string" + }, + "use": { + "description": "JSON Web Key Use\n\nThe \"use\" (public key use) parameter identifies the intended use of\nthe public key. The \"use\" parameter is employed to indicate whether\na public key is used for encrypting data or verifying the signature\non data. Valid values are \"enc\" and \"sig\".", + "type": "string" + } + }, + "required": [ + "alg", + "use", + "kid" + ], + "type": "object" + }, + "errorOAuth2": { + "description": "Error", + "properties": { + "error": { + "description": "Error", + "type": "string" + }, + "error_debug": { + "description": "Error Debug Information\n\nOnly available in dev mode.", + "type": "string" + }, + "error_description": { + "description": "Error Description", + "type": "string" + }, + "error_hint": { + "description": "Error Hint\n\nHelps the user identify the error cause.", + "example": "The redirect URL is not allowed.", + "type": "string" + }, + "status_code": { + "description": "HTTP Status Code", + "example": 401, + "format": "int64", + "type": "integer" + } + }, + "type": "object" + }, + "genericError": { + "properties": { + "code": { + "description": "The status code", + "example": 404, + "format": "int64", + "type": "integer" + }, + "debug": { + "description": "Debug information\n\nThis field is often not exposed to protect against leaking\nsensitive information.", + "example": "SQL field \"foo\" is not a bool.", + "type": "string" + }, + "details": { + "description": "Further error details" + }, + "id": { + "description": "The error ID\n\nUseful when trying to identify various errors in application logic.", + "type": "string" + }, + "message": { + "description": "Error message\n\nThe error's message.", + "example": "The resource could not be found", + "type": "string" + }, + "reason": { + "description": "A human-readable reason for the error", + "example": "User with ID 1234 does not exist.", + "type": "string" + }, + "request": { + "description": "The request ID\n\nThe request ID is often exposed internally in order to trace\nerrors across service architectures. This is often a UUID.", + "example": "d7ef54b1-ec15-46e6-bccb-524b82c035e6", + "type": "string" + }, + "status": { + "description": "The status description", + "example": "Not Found", + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "healthNotReadyStatus": { + "properties": { + "errors": { + "additionalProperties": { + "type": "string" + }, + "description": "Errors contains a list of errors that caused the not ready status.", + "type": "object" + } + }, + "type": "object" + }, + "healthStatus": { + "properties": { + "status": { + "description": "Status always contains \"ok\".", + "type": "string" + } + }, + "type": "object" + }, + "introspectedOAuth2Token": { + "description": "Introspection contains an access token's session data as specified by\n[IETF RFC 7662](https://tools.ietf.org/html/rfc7662)", + "properties": { + "active": { + "description": "Active is a boolean indicator of whether or not the presented token\nis currently active. The specifics of a token's \"active\" state\nwill vary depending on the implementation of the authorization\nserver and the information it keeps about its tokens, but a \"true\"\nvalue return for the \"active\" property will generally indicate\nthat a given token has been issued by this authorization server,\nhas not been revoked by the resource owner, and is within its\ngiven time window of validity (e.g., after its issuance time and\nbefore its expiration time).", + "type": "boolean" + }, + "aud": { + "description": "Audience contains a list of the token's intended audiences.", + "items": { + "type": "string" + }, + "type": "array" + }, + "client_id": { + "description": "ID is aclient identifier for the OAuth 2.0 client that\nrequested this token.", + "type": "string" + }, + "exp": { + "description": "Expires at is an integer timestamp, measured in the number of seconds\nsince January 1 1970 UTC, indicating when this token will expire.", + "format": "int64", + "type": "integer" + }, + "ext": { + "additionalProperties": {}, + "description": "Extra is arbitrary data set by the session.", + "type": "object" + }, + "iat": { + "description": "Issued at is an integer timestamp, measured in the number of seconds\nsince January 1 1970 UTC, indicating when this token was\noriginally issued.", + "format": "int64", + "type": "integer" + }, + "iss": { + "description": "IssuerURL is a string representing the issuer of this token", + "type": "string" + }, + "nbf": { + "description": "NotBefore is an integer timestamp, measured in the number of seconds\nsince January 1 1970 UTC, indicating when this token is not to be\nused before.", + "format": "int64", + "type": "integer" + }, + "obfuscated_subject": { + "description": "ObfuscatedSubject is set when the subject identifier algorithm was set to \"pairwise\" during authorization.\nIt is the `sub` value of the ID Token that was issued.", + "type": "string" + }, + "scope": { + "description": "Scope is a JSON string containing a space-separated list of\nscopes associated with this token.", + "type": "string" + }, + "sub": { + "description": "Subject of the token, as defined in JWT [RFC7519].\nUsually a machine-readable identifier of the resource owner who\nauthorized this token.", + "type": "string" + }, + "token_type": { + "description": "TokenType is the introspected token's type, typically `Bearer`.", + "type": "string" + }, + "token_use": { + "description": "TokenUse is the introspected token's use, for example `access_token` or `refresh_token`.", + "type": "string" + }, + "username": { + "description": "Username is a human-readable identifier for the resource owner who\nauthorized this token.", + "type": "string" + } + }, + "required": [ + "active" + ], + "type": "object" + }, + "jsonPatch": { + "description": "A JSONPatch document as defined by RFC 6902", + "properties": { + "from": { + "description": "This field is used together with operation \"move\" and uses JSON Pointer notation.\n\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).", + "example": "/name", + "type": "string" + }, + "op": { + "description": "The operation to be performed. One of \"add\", \"remove\", \"replace\", \"move\", \"copy\", or \"test\".", + "example": "replace", + "type": "string" + }, + "path": { + "description": "The path to the target path. Uses JSON pointer notation.\n\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).", + "example": "/name", + "type": "string" + }, + "value": { + "description": "The value to be used within the operations.\n\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).", + "example": "foobar" + } + }, + "required": [ + "op", + "path" + ], + "type": "object" + }, + "jsonPatchDocument": { + "description": "A JSONPatchDocument request", + "items": { + "$ref": "#/components/schemas/jsonPatch" + }, + "type": "array" + }, + "jsonWebKey": { + "properties": { + "alg": { + "description": "The \"alg\" (algorithm) parameter identifies the algorithm intended for\nuse with the key. The values used should either be registered in the\nIANA \"JSON Web Signature and Encryption Algorithms\" registry\nestablished by [JWA] or be a value that contains a Collision-\nResistant Name.", + "example": "RS256", + "type": "string" + }, + "crv": { + "example": "P-256", + "type": "string" + }, + "d": { + "example": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "type": "string" + }, + "dp": { + "example": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "type": "string" + }, + "dq": { + "example": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "type": "string" + }, + "e": { + "example": "AQAB", + "type": "string" + }, + "k": { + "example": "GawgguFyGrWKav7AX4VKUg", + "type": "string" + }, + "kid": { + "description": "The \"kid\" (key ID) parameter is used to match a specific key. This\nis used, for instance, to choose among a set of keys within a JWK Set\nduring key rollover. The structure of the \"kid\" value is\nunspecified. When \"kid\" values are used within a JWK Set, different\nkeys within the JWK Set SHOULD use distinct \"kid\" values. (One\nexample in which different keys might use the same \"kid\" value is if\nthey have different \"kty\" (key type) values but are considered to be\nequivalent alternatives by the application using them.) The \"kid\"\nvalue is a case-sensitive string.", + "example": "1603dfe0af8f4596", + "type": "string" + }, + "kty": { + "description": "The \"kty\" (key type) parameter identifies the cryptographic algorithm\nfamily used with the key, such as \"RSA\" or \"EC\". \"kty\" values should\neither be registered in the IANA \"JSON Web Key Types\" registry\nestablished by [JWA] or be a value that contains a Collision-\nResistant Name. The \"kty\" value is a case-sensitive string.", + "example": "RSA", + "type": "string" + }, + "n": { + "example": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "type": "string" + }, + "p": { + "example": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "type": "string" + }, + "q": { + "example": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "type": "string" + }, + "qi": { + "example": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "type": "string" + }, + "use": { + "description": "Use (\"public key use\") identifies the intended use of\nthe public key. The \"use\" parameter is employed to indicate whether\na public key is used for encrypting data or verifying the signature\non data. Values are commonly \"sig\" (signature) or \"enc\" (encryption).", + "example": "sig", + "type": "string" + }, + "x": { + "example": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "type": "string" + }, + "x5c": { + "description": "The \"x5c\" (X.509 certificate chain) parameter contains a chain of one\nor more PKIX certificates [RFC5280]. The certificate chain is\nrepresented as a JSON array of certificate value strings. Each\nstring in the array is a base64-encoded (Section 4 of [RFC4648] --\nnot base64url-encoded) DER [ITU.X690.1994] PKIX certificate value.\nThe PKIX certificate containing the key value MUST be the first\ncertificate.", + "items": { + "type": "string" + }, + "type": "array" + }, + "y": { + "example": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0", + "type": "string" + } + }, + "required": [ + "use", + "kty", + "kid", + "alg" + ], + "type": "object" + }, + "jsonWebKeySet": { + "description": "JSON Web Key Set", + "properties": { + "keys": { + "description": "List of JSON Web Keys\n\nThe value of the \"keys\" parameter is an array of JSON Web Key (JWK)\nvalues. By default, the order of the JWK values within the array does\nnot imply an order of preference among them, although applications\nof JWK Sets can choose to assign a meaning to the order for their\npurposes, if desired.", + "items": { + "$ref": "#/components/schemas/jsonWebKey" + }, + "type": "array" + } + }, + "type": "object" + }, + "nullDuration": { + "nullable": true, + "pattern": "^[0-9]+(ns|us|ms|s|m|h)$", + "type": "string" + }, + "nullInt64": { + "nullable": true, + "type": "integer" + }, + "nullTime": { + "format": "date-time", + "title": "NullTime implements sql.NullTime functionality.", + "type": "string" + }, + "oAuth2Client": { + "description": "OAuth 2.0 Clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "properties": { + "access_token_strategy": { + "description": "OAuth 2.0 Access Token Strategy\n\nAccessTokenStrategy is the strategy used to generate access tokens.\nValid options are `jwt` and `opaque`. `jwt` is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens\nSetting the stragegy here overrides the global setting in `strategies.access_token`.", + "type": "string" + }, + "allowed_cors_origins": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "authorization_code_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "authorization_code_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "authorization_code_grant_refresh_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "backchannel_logout_session_required": { + "description": "OpenID Connect Back-Channel Logout Session Required\n\nBoolean value specifying whether the RP requires that a sid (session ID) Claim be included in the Logout\nToken to identify the RP session with the OP when the backchannel_logout_uri is used.\nIf omitted, the default value is false.", + "type": "boolean" + }, + "backchannel_logout_uri": { + "description": "OpenID Connect Back-Channel Logout URI\n\nRP URL that will cause the RP to log itself out when sent a Logout Token by the OP.", + "type": "string" + }, + "client_credentials_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "client_id": { + "description": "OAuth 2.0 Client ID\n\nThe ID is autogenerated and immutable.", + "type": "string" + }, + "client_name": { + "description": "OAuth 2.0 Client Name\n\nThe human-readable name of the client to be presented to the\nend-user during authorization.", + "type": "string" + }, + "client_secret": { + "description": "OAuth 2.0 Client Secret\n\nThe secret will be included in the create request as cleartext, and then\nnever again. The secret is kept in hashed format and is not recoverable once lost.", + "type": "string" + }, + "client_secret_expires_at": { + "description": "OAuth 2.0 Client Secret Expires At\n\nThe field is currently not supported and its value is always 0.", + "format": "int64", + "type": "integer" + }, + "client_uri": { + "description": "OAuth 2.0 Client URI\n\nClientURI is a URL string of a web page providing information about the client.\nIf present, the server SHOULD display this URL to the end-user in\na clickable fashion.", + "type": "string" + }, + "contacts": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "created_at": { + "description": "OAuth 2.0 Client Creation Date\n\nCreatedAt returns the timestamp of the client's creation.", + "format": "date-time", + "type": "string" + }, + "frontchannel_logout_session_required": { + "description": "OpenID Connect Front-Channel Logout Session Required\n\nBoolean value specifying whether the RP requires that iss (issuer) and sid (session ID) query parameters be\nincluded to identify the RP session with the OP when the frontchannel_logout_uri is used.\nIf omitted, the default value is false.", + "type": "boolean" + }, + "frontchannel_logout_uri": { + "description": "OpenID Connect Front-Channel Logout URI\n\nRP URL that will cause the RP to log itself out when rendered in an iframe by the OP. An iss (issuer) query\nparameter and a sid (session ID) query parameter MAY be included by the OP to enable the RP to validate the\nrequest and to determine which of the potentially multiple sessions is to be logged out; if either is\nincluded, both MUST be.", + "type": "string" + }, + "grant_types": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "implicit_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "implicit_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "jwks": { + "description": "OAuth 2.0 Client JSON Web Key Set\n\nClient's JSON Web Key Set [JWK] document, passed by value. The semantics of the jwks parameter are the same as\nthe jwks_uri parameter, other than that the JWK Set is passed by value, rather than by reference. This parameter\nis intended only to be used by Clients that, for some reason, are unable to use the jwks_uri parameter, for\ninstance, by native applications that might not have a location to host the contents of the JWK Set. If a Client\ncan use jwks_uri, it MUST NOT use jwks. One significant downside of jwks is that it does not enable key rotation\n(which jwks_uri does, as described in Section 10 of OpenID Connect Core 1.0 [OpenID.Core]). The jwks_uri and jwks\nparameters MUST NOT be used together." + }, + "jwks_uri": { + "description": "OAuth 2.0 Client JSON Web Key Set URL\n\nURL for the Client's JSON Web Key Set [JWK] document. If the Client signs requests to the Server, it contains\nthe signing key(s) the Server uses to validate signatures from the Client. The JWK Set MAY also contain the\nClient's encryption keys(s), which are used by the Server to encrypt responses to the Client. When both signing\nand encryption keys are made available, a use (Key Use) parameter value is REQUIRED for all keys in the referenced\nJWK Set to indicate each key's intended usage. Although some algorithms allow the same key to be used for both\nsignatures and encryption, doing so is NOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used\nto provide X.509 representations of keys provided. When used, the bare key values MUST still be present and MUST\nmatch those in the certificate.", + "type": "string" + }, + "jwt_bearer_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "logo_uri": { + "description": "OAuth 2.0 Client Logo URI\n\nA URL string referencing the client's logo.", + "type": "string" + }, + "metadata": { + "$ref": "#/components/schemas/JSONRawMessage" + }, + "owner": { + "description": "OAuth 2.0 Client Owner\n\nOwner is a string identifying the owner of the OAuth 2.0 Client.", + "type": "string" + }, + "policy_uri": { + "description": "OAuth 2.0 Client Policy URI\n\nPolicyURI is a URL string that points to a human-readable privacy policy document\nthat describes how the deployment organization collects, uses,\nretains, and discloses personal data.", + "type": "string" + }, + "post_logout_redirect_uris": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "redirect_uris": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "refresh_token_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_refresh_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "registration_access_token": { + "description": "OpenID Connect Dynamic Client Registration Access Token\n\nRegistrationAccessToken can be used to update, get, or delete the OAuth2 Client. It is sent when creating a client\nusing Dynamic Client Registration.", + "type": "string" + }, + "registration_client_uri": { + "description": "OpenID Connect Dynamic Client Registration URL\n\nRegistrationClientURI is the URL used to update, get, or delete the OAuth2 Client.", + "type": "string" + }, + "request_object_signing_alg": { + "description": "OpenID Connect Request Object Signing Algorithm\n\nJWS [JWS] alg algorithm [JWA] that MUST be used for signing Request Objects sent to the OP. All Request Objects\nfrom this Client MUST be rejected, if not signed with this algorithm.", + "type": "string" + }, + "request_uris": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "response_types": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "scope": { + "description": "OAuth 2.0 Client Scope\n\nScope is a string containing a space-separated list of scope values (as\ndescribed in Section 3.3 of OAuth 2.0 [RFC6749]) that the client\ncan use when requesting access tokens.", + "example": "scope1 scope-2 scope.3 scope:4", + "type": "string" + }, + "sector_identifier_uri": { + "description": "OpenID Connect Sector Identifier URI\n\nURL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a\nfile with a single JSON array of redirect_uri values.", + "type": "string" + }, + "skip_consent": { + "description": "SkipConsent skips the consent screen for this client. This field can only\nbe set from the admin API.", + "type": "boolean" + }, + "subject_type": { + "description": "OpenID Connect Subject Type\n\nThe `subject_types_supported` Discovery parameter contains a\nlist of the supported subject_type values for this server. Valid types include `pairwise` and `public`.", + "type": "string" + }, + "token_endpoint_auth_method": { + "default": "client_secret_basic", + "description": "OAuth 2.0 Token Endpoint Authentication Method\n\nRequested Client Authentication method for the Token Endpoint. The options are:\n\n`client_secret_basic`: (default) Send `client_id` and `client_secret` as `application/x-www-form-urlencoded` encoded in the HTTP Authorization header.\n`client_secret_post`: Send `client_id` and `client_secret` as `application/x-www-form-urlencoded` in the HTTP body.\n`private_key_jwt`: Use JSON Web Tokens to authenticate the client.\n`none`: Used for public clients (native apps, mobile apps) which can not have secrets.", + "type": "string" + }, + "token_endpoint_auth_signing_alg": { + "description": "OAuth 2.0 Token Endpoint Signing Algorithm\n\nRequested Client Authentication signing algorithm for the Token Endpoint.", + "type": "string" + }, + "tos_uri": { + "description": "OAuth 2.0 Client Terms of Service URI\n\nA URL string pointing to a human-readable terms of service\ndocument for the client that describes a contractual relationship\nbetween the end-user and the client that the end-user accepts when\nauthorizing the client.", + "type": "string" + }, + "updated_at": { + "description": "OAuth 2.0 Client Last Update Date\n\nUpdatedAt returns the timestamp of the last update.", + "format": "date-time", + "type": "string" + }, + "userinfo_signed_response_alg": { + "description": "OpenID Connect Request Userinfo Signed Response Algorithm\n\nJWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT\n[JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims\nas a UTF-8 encoded JSON object using the application/json content-type.", + "type": "string" + } + }, + "title": "OAuth 2.0 Client", + "type": "object" + }, + "oAuth2ClientTokenLifespans": { + "description": "Lifespans of different token types issued for this OAuth 2.0 Client.", + "properties": { + "authorization_code_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "authorization_code_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "authorization_code_grant_refresh_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "client_credentials_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "implicit_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "implicit_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "jwt_bearer_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_refresh_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + } + }, + "title": "OAuth 2.0 Client Token Lifespans", + "type": "object" + }, + "oAuth2ConsentRequest": { + "properties": { + "acr": { + "description": "ACR represents the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it\nto express that, for example, a user authenticated using two factor authentication.", + "type": "string" + }, + "amr": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "challenge": { + "description": "ID is the identifier (\"authorization challenge\") of the consent authorization request. It is used to\nidentify the session.", + "type": "string" + }, + "client": { + "$ref": "#/components/schemas/oAuth2Client" + }, + "context": { + "$ref": "#/components/schemas/JSONRawMessage" + }, + "login_challenge": { + "description": "LoginChallenge is the login challenge this consent challenge belongs to. It can be used to associate\na login and consent request in the login & consent app.", + "type": "string" + }, + "login_session_id": { + "description": "LoginSessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag)\nthis ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false)\nthis will be a new random value. This value is used as the \"sid\" parameter in the ID Token and in OIDC Front-/Back-\nchannel logout. It's value can generally be used to associate consecutive login requests by a certain user.", + "type": "string" + }, + "oidc_context": { + "$ref": "#/components/schemas/oAuth2ConsentRequestOpenIDConnectContext" + }, + "request_url": { + "description": "RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which\ninitiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but\nmight come in handy if you want to deal with additional request parameters.", + "type": "string" + }, + "requested_access_token_audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "requested_scope": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "skip": { + "description": "Skip, if true, implies that the client has requested the same scopes from the same user previously.\nIf true, you must not ask the user to grant the requested scopes. You must however either allow or deny the\nconsent request using the usual API call.", + "type": "boolean" + }, + "subject": { + "description": "Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope\nrequested by the OAuth 2.0 client.", + "type": "string" + } + }, + "required": [ + "challenge" + ], + "title": "Contains information on an ongoing consent request.", + "type": "object" + }, + "oAuth2ConsentRequestOpenIDConnectContext": { + "properties": { + "acr_values": { + "description": "ACRValues is the Authentication AuthorizationContext Class Reference requested in the OAuth 2.0 Authorization request.\nIt is a parameter defined by OpenID Connect and expresses which level of authentication (e.g. 2FA) is required.\n\nOpenID Connect defines it as follows:\n> Requested Authentication AuthorizationContext Class Reference values. Space-separated string that specifies the acr values\nthat the Authorization Server is being requested to use for processing this Authentication Request, with the\nvalues appearing in order of preference. The Authentication AuthorizationContext Class satisfied by the authentication\nperformed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a\nVoluntary Claim by this parameter.", + "items": { + "type": "string" + }, + "type": "array" + }, + "display": { + "description": "Display is a string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User.\nThe defined values are:\npage: The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. If the display parameter is not specified, this is the default display mode.\npopup: The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over.\ntouch: The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface.\nwap: The Authorization Server SHOULD display the authentication and consent UI consistent with a \"feature phone\" type display.\n\nThe Authorization Server MAY also attempt to detect the capabilities of the User Agent and present an appropriate display.", + "type": "string" + }, + "id_token_hint_claims": { + "additionalProperties": {}, + "description": "IDTokenHintClaims are the claims of the ID Token previously issued by the Authorization Server being passed as a hint about the\nEnd-User's current or past authenticated session with the Client.", + "type": "object" + }, + "login_hint": { + "description": "LoginHint hints about the login identifier the End-User might use to log in (if necessary).\nThis hint can be used by an RP if it first asks the End-User for their e-mail address (or other identifier)\nand then wants to pass that value as a hint to the discovered authorization service. This value MAY also be a\nphone number in the format specified for the phone_number Claim. The use of this parameter is optional.", + "type": "string" + }, + "ui_locales": { + "description": "UILocales is the End-User'id preferred languages and scripts for the user interface, represented as a\nspace-separated list of BCP47 [RFC5646] language tag values, ordered by preference. For instance, the value\n\"fr-CA fr en\" represents a preference for French as spoken in Canada, then French (without a region designation),\nfollowed by English (without a region designation). An error SHOULD NOT result if some or all of the requested\nlocales are not supported by the OpenID Provider.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "title": "Contains optional information about the OpenID Connect request.", + "type": "object" + }, + "oAuth2ConsentSession": { + "description": "A completed OAuth 2.0 Consent Session.", + "properties": { + "consent_request": { + "$ref": "#/components/schemas/oAuth2ConsentRequest" + }, + "expires_at": { + "properties": { + "access_token": { + "format": "date-time", + "type": "string" + }, + "authorize_code": { + "format": "date-time", + "type": "string" + }, + "id_token": { + "format": "date-time", + "type": "string" + }, + "par_context": { + "format": "date-time", + "type": "string" + }, + "refresh_token": { + "format": "date-time", + "type": "string" + } + }, + "type": "object" + }, + "grant_access_token_audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "grant_scope": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "handled_at": { + "$ref": "#/components/schemas/nullTime" + }, + "remember": { + "description": "Remember Consent\n\nRemember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same\nclient asks the same user for the same, or a subset of, scope.", + "type": "boolean" + }, + "remember_for": { + "description": "Remember Consent For\n\nRememberFor sets how long the consent authorization should be remembered for in seconds. If set to `0`, the\nauthorization will be remembered indefinitely.", + "format": "int64", + "type": "integer" + }, + "session": { + "$ref": "#/components/schemas/acceptOAuth2ConsentRequestSession" + } + }, + "title": "OAuth 2.0 Consent Session", + "type": "object" + }, + "oAuth2ConsentSessions": { + "description": "List of OAuth 2.0 Consent Sessions", + "items": { + "$ref": "#/components/schemas/oAuth2ConsentSession" + }, + "type": "array" + }, + "oAuth2LoginRequest": { + "properties": { + "challenge": { + "description": "ID is the identifier (\"login challenge\") of the login request. It is used to\nidentify the session.", + "type": "string" + }, + "client": { + "$ref": "#/components/schemas/oAuth2Client" + }, + "oidc_context": { + "$ref": "#/components/schemas/oAuth2ConsentRequestOpenIDConnectContext" + }, + "request_url": { + "description": "RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which\ninitiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but\nmight come in handy if you want to deal with additional request parameters.", + "type": "string" + }, + "requested_access_token_audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "requested_scope": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "session_id": { + "description": "SessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag)\nthis ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false)\nthis will be a new random value. This value is used as the \"sid\" parameter in the ID Token and in OIDC Front-/Back-\nchannel logout. It's value can generally be used to associate consecutive login requests by a certain user.", + "type": "string" + }, + "skip": { + "description": "Skip, if true, implies that the client has requested the same scopes from the same user previously.\nIf true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL.\n\nThis feature allows you to update / set session information.", + "type": "boolean" + }, + "subject": { + "description": "Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope\nrequested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type\nwhen accepting the login request, or the request will fail.", + "type": "string" + } + }, + "required": [ + "challenge", + "requested_scope", + "requested_access_token_audience", + "skip", + "subject", + "client", + "request_url" + ], + "title": "Contains information on an ongoing login request.", + "type": "object" + }, + "oAuth2LogoutRequest": { + "properties": { + "challenge": { + "description": "Challenge is the identifier (\"logout challenge\") of the logout authentication request. It is used to\nidentify the session.", + "type": "string" + }, + "client": { + "$ref": "#/components/schemas/oAuth2Client" + }, + "request_url": { + "description": "RequestURL is the original Logout URL requested.", + "type": "string" + }, + "rp_initiated": { + "description": "RPInitiated is set to true if the request was initiated by a Relying Party (RP), also known as an OAuth 2.0 Client.", + "type": "boolean" + }, + "sid": { + "description": "SessionID is the login session ID that was requested to log out.", + "type": "string" + }, + "subject": { + "description": "Subject is the user for whom the logout was request.", + "type": "string" + } + }, + "title": "Contains information about an ongoing logout request.", + "type": "object" + }, + "oAuth2RedirectTo": { + "description": "Contains a redirect URL used to complete a login, consent, or logout request.", + "properties": { + "redirect_to": { + "description": "RedirectURL is the URL which you should redirect the user's browser to once the authentication process is completed.", + "type": "string" + } + }, + "required": [ + "redirect_to" + ], + "title": "OAuth 2.0 Redirect Browser To", + "type": "object" + }, + "oAuth2TokenExchange": { + "description": "OAuth2 Token Exchange Result", + "properties": { + "access_token": { + "description": "The access token issued by the authorization server.", + "type": "string" + }, + "expires_in": { + "description": "The lifetime in seconds of the access token. For\nexample, the value \"3600\" denotes that the access token will\nexpire in one hour from the time the response was generated.", + "format": "int64", + "type": "integer" + }, + "id_token": { + "description": "To retrieve a refresh token request the id_token scope.", + "format": "int64", + "type": "integer" + }, + "refresh_token": { + "description": "The refresh token, which can be used to obtain new\naccess tokens. To retrieve it add the scope \"offline\" to your access token request.", + "type": "string" + }, + "scope": { + "description": "The scope of the access token", + "type": "string" + }, + "token_type": { + "description": "The type of the token issued", + "type": "string" + } + }, + "type": "object" + }, + "oidcConfiguration": { + "description": "Includes links to several endpoints (for example `/oauth2/token`) and exposes information on supported signature algorithms\namong others.", + "properties": { + "authorization_endpoint": { + "description": "OAuth 2.0 Authorization Endpoint URL", + "example": "https://playground.ory.sh/ory-hydra/public/oauth2/auth", + "type": "string" + }, + "backchannel_logout_session_supported": { + "description": "OpenID Connect Back-Channel Logout Session Required\n\nBoolean value specifying whether the OP can pass a sid (session ID) Claim in the Logout Token to identify the RP\nsession with the OP. If supported, the sid Claim is also included in ID Tokens issued by the OP", + "type": "boolean" + }, + "backchannel_logout_supported": { + "description": "OpenID Connect Back-Channel Logout Supported\n\nBoolean value specifying whether the OP supports back-channel logout, with true indicating support.", + "type": "boolean" + }, + "claims_parameter_supported": { + "description": "OpenID Connect Claims Parameter Parameter Supported\n\nBoolean value specifying whether the OP supports use of the claims parameter, with true indicating support.", + "type": "boolean" + }, + "claims_supported": { + "description": "OpenID Connect Supported Claims\n\nJSON array containing a list of the Claim Names of the Claims that the OpenID Provider MAY be able to supply\nvalues for. Note that for privacy or other reasons, this might not be an exhaustive list.", + "items": { + "type": "string" + }, + "type": "array" + }, + "code_challenge_methods_supported": { + "description": "OAuth 2.0 PKCE Supported Code Challenge Methods\n\nJSON array containing a list of Proof Key for Code Exchange (PKCE) [RFC7636] code challenge methods supported\nby this authorization server.", + "items": { + "type": "string" + }, + "type": "array" + }, + "end_session_endpoint": { + "description": "OpenID Connect End-Session Endpoint\n\nURL at the OP to which an RP can perform a redirect to request that the End-User be logged out at the OP.", + "type": "string" + }, + "frontchannel_logout_session_supported": { + "description": "OpenID Connect Front-Channel Logout Session Required\n\nBoolean value specifying whether the OP can pass iss (issuer) and sid (session ID) query parameters to identify\nthe RP session with the OP when the frontchannel_logout_uri is used. If supported, the sid Claim is also\nincluded in ID Tokens issued by the OP.", + "type": "boolean" + }, + "frontchannel_logout_supported": { + "description": "OpenID Connect Front-Channel Logout Supported\n\nBoolean value specifying whether the OP supports HTTP-based logout, with true indicating support.", + "type": "boolean" + }, + "grant_types_supported": { + "description": "OAuth 2.0 Supported Grant Types\n\nJSON array containing a list of the OAuth 2.0 Grant Type values that this OP supports.", + "items": { + "type": "string" + }, + "type": "array" + }, + "id_token_signed_response_alg": { + "description": "OpenID Connect Default ID Token Signing Algorithms\n\nAlgorithm used to sign OpenID Connect ID Tokens.", + "items": { + "type": "string" + }, + "type": "array" + }, + "id_token_signing_alg_values_supported": { + "description": "OpenID Connect Supported ID Token Signing Algorithms\n\nJSON array containing a list of the JWS signing algorithms (alg values) supported by the OP for the ID Token\nto encode the Claims in a JWT.", + "items": { + "type": "string" + }, + "type": "array" + }, + "issuer": { + "description": "OpenID Connect Issuer URL\n\nAn URL using the https scheme with no query or fragment component that the OP asserts as its IssuerURL Identifier.\nIf IssuerURL discovery is supported , this value MUST be identical to the issuer value returned\nby WebFinger. This also MUST be identical to the iss Claim value in ID Tokens issued from this IssuerURL.", + "example": "https://playground.ory.sh/ory-hydra/public/", + "type": "string" + }, + "jwks_uri": { + "description": "OpenID Connect Well-Known JSON Web Keys URL\n\nURL of the OP's JSON Web Key Set [JWK] document. This contains the signing key(s) the RP uses to validate\nsignatures from the OP. The JWK Set MAY also contain the Server's encryption key(s), which are used by RPs\nto encrypt requests to the Server. When both signing and encryption keys are made available, a use (Key Use)\nparameter value is REQUIRED for all keys in the referenced JWK Set to indicate each key's intended usage.\nAlthough some algorithms allow the same key to be used for both signatures and encryption, doing so is\nNOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of\nkeys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.", + "example": "https://{slug}.projects.oryapis.com/.well-known/jwks.json", + "type": "string" + }, + "registration_endpoint": { + "description": "OpenID Connect Dynamic Client Registration Endpoint URL", + "example": "https://playground.ory.sh/ory-hydra/admin/client", + "type": "string" + }, + "request_object_signing_alg_values_supported": { + "description": "OpenID Connect Supported Request Object Signing Algorithms\n\nJSON array containing a list of the JWS signing algorithms (alg values) supported by the OP for Request Objects,\nwhich are described in Section 6.1 of OpenID Connect Core 1.0 [OpenID.Core]. These algorithms are used both when\nthe Request Object is passed by value (using the request parameter) and when it is passed by reference\n(using the request_uri parameter).", + "items": { + "type": "string" + }, + "type": "array" + }, + "request_parameter_supported": { + "description": "OpenID Connect Request Parameter Supported\n\nBoolean value specifying whether the OP supports use of the request parameter, with true indicating support.", + "type": "boolean" + }, + "request_uri_parameter_supported": { + "description": "OpenID Connect Request URI Parameter Supported\n\nBoolean value specifying whether the OP supports use of the request_uri parameter, with true indicating support.", + "type": "boolean" + }, + "require_request_uri_registration": { + "description": "OpenID Connect Requires Request URI Registration\n\nBoolean value specifying whether the OP requires any request_uri values used to be pre-registered\nusing the request_uris registration parameter.", + "type": "boolean" + }, + "response_modes_supported": { + "description": "OAuth 2.0 Supported Response Modes\n\nJSON array containing a list of the OAuth 2.0 response_mode values that this OP supports.", + "items": { + "type": "string" + }, + "type": "array" + }, + "response_types_supported": { + "description": "OAuth 2.0 Supported Response Types\n\nJSON array containing a list of the OAuth 2.0 response_type values that this OP supports. Dynamic OpenID\nProviders MUST support the code, id_token, and the token id_token Response Type values.", + "items": { + "type": "string" + }, + "type": "array" + }, + "revocation_endpoint": { + "description": "OAuth 2.0 Token Revocation URL\n\nURL of the authorization server's OAuth 2.0 revocation endpoint.", + "type": "string" + }, + "scopes_supported": { + "description": "OAuth 2.0 Supported Scope Values\n\nJSON array containing a list of the OAuth 2.0 [RFC6749] scope values that this server supports. The server MUST\nsupport the openid scope value. Servers MAY choose not to advertise some supported scope values even when this parameter is used", + "items": { + "type": "string" + }, + "type": "array" + }, + "subject_types_supported": { + "description": "OpenID Connect Supported Subject Types\n\nJSON array containing a list of the Subject Identifier types that this OP supports. Valid types include\npairwise and public.", + "items": { + "type": "string" + }, + "type": "array" + }, + "token_endpoint": { + "description": "OAuth 2.0 Token Endpoint URL", + "example": "https://playground.ory.sh/ory-hydra/public/oauth2/token", + "type": "string" + }, + "token_endpoint_auth_methods_supported": { + "description": "OAuth 2.0 Supported Client Authentication Methods\n\nJSON array containing a list of Client Authentication methods supported by this Token Endpoint. The options are\nclient_secret_post, client_secret_basic, client_secret_jwt, and private_key_jwt, as described in Section 9 of OpenID Connect Core 1.0", + "items": { + "type": "string" + }, + "type": "array" + }, + "userinfo_endpoint": { + "description": "OpenID Connect Userinfo URL\n\nURL of the OP's UserInfo Endpoint.", + "type": "string" + }, + "userinfo_signed_response_alg": { + "description": "OpenID Connect User Userinfo Signing Algorithm\n\nAlgorithm used to sign OpenID Connect Userinfo Responses.", + "items": { + "type": "string" + }, + "type": "array" + }, + "userinfo_signing_alg_values_supported": { + "description": "OpenID Connect Supported Userinfo Signing Algorithm\n\nJSON array containing a list of the JWS [JWS] signing algorithms (alg values) [JWA] supported by the UserInfo Endpoint to encode the Claims in a JWT [JWT].", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "issuer", + "authorization_endpoint", + "token_endpoint", + "jwks_uri", + "subject_types_supported", + "response_types_supported", + "id_token_signing_alg_values_supported", + "id_token_signed_response_alg", + "userinfo_signed_response_alg" + ], + "title": "OpenID Connect Discovery Metadata", + "type": "object" + }, + "oidcUserInfo": { + "description": "OpenID Connect Userinfo", + "properties": { + "birthdate": { + "description": "End-User's birthday, represented as an ISO 8601:2004 [ISO8601‑2004] YYYY-MM-DD format. The year MAY be 0000, indicating that it is omitted. To represent only the year, YYYY format is allowed. Note that depending on the underlying platform's date related function, providing just year can result in varying month and day, so the implementers need to take this factor into account to correctly process the dates.", + "type": "string" + }, + "email": { + "description": "End-User's preferred e-mail address. Its value MUST conform to the RFC 5322 [RFC5322] addr-spec syntax. The RP MUST NOT rely upon this value being unique, as discussed in Section 5.7.", + "type": "string" + }, + "email_verified": { + "description": "True if the End-User's e-mail address has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this e-mail address was controlled by the End-User at the time the verification was performed. The means by which an e-mail address is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating.", + "type": "boolean" + }, + "family_name": { + "description": "Surname(s) or last name(s) of the End-User. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters.", + "type": "string" + }, + "gender": { + "description": "End-User's gender. Values defined by this specification are female and male. Other values MAY be used when neither of the defined values are applicable.", + "type": "string" + }, + "given_name": { + "description": "Given name(s) or first name(s) of the End-User. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters.", + "type": "string" + }, + "locale": { + "description": "End-User's locale, represented as a BCP47 [RFC5646] language tag. This is typically an ISO 639-1 Alpha-2 [ISO639‑1] language code in lowercase and an ISO 3166-1 Alpha-2 [ISO3166‑1] country code in uppercase, separated by a dash. For example, en-US or fr-CA. As a compatibility note, some implementations have used an underscore as the separator rather than a dash, for example, en_US; Relying Parties MAY choose to accept this locale syntax as well.", + "type": "string" + }, + "middle_name": { + "description": "Middle name(s) of the End-User. Note that in some cultures, people can have multiple middle names; all can be present, with the names being separated by space characters. Also note that in some cultures, middle names are not used.", + "type": "string" + }, + "name": { + "description": "End-User's full name in displayable form including all name parts, possibly including titles and suffixes, ordered according to the End-User's locale and preferences.", + "type": "string" + }, + "nickname": { + "description": "Casual name of the End-User that may or may not be the same as the given_name. For instance, a nickname value of Mike might be returned alongside a given_name value of Michael.", + "type": "string" + }, + "phone_number": { + "description": "End-User's preferred telephone number. E.164 [E.164] is RECOMMENDED as the format of this Claim, for example, +1 (425) 555-1212 or +56 (2) 687 2400. If the phone number contains an extension, it is RECOMMENDED that the extension be represented using the RFC 3966 [RFC3966] extension syntax, for example, +1 (604) 555-1234;ext=5678.", + "type": "string" + }, + "phone_number_verified": { + "description": "True if the End-User's phone number has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this phone number was controlled by the End-User at the time the verification was performed. The means by which a phone number is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating. When true, the phone_number Claim MUST be in E.164 format and any extensions MUST be represented in RFC 3966 format.", + "type": "boolean" + }, + "picture": { + "description": "URL of the End-User's profile picture. This URL MUST refer to an image file (for example, a PNG, JPEG, or GIF image file), rather than to a Web page containing an image. Note that this URL SHOULD specifically reference a profile photo of the End-User suitable for displaying when describing the End-User, rather than an arbitrary photo taken by the End-User.", + "type": "string" + }, + "preferred_username": { + "description": "Non-unique shorthand name by which the End-User wishes to be referred to at the RP, such as janedoe or j.doe. This value MAY be any valid JSON string including special characters such as @, /, or whitespace.", + "type": "string" + }, + "profile": { + "description": "URL of the End-User's profile page. The contents of this Web page SHOULD be about the End-User.", + "type": "string" + }, + "sub": { + "description": "Subject - Identifier for the End-User at the IssuerURL.", + "type": "string" + }, + "updated_at": { + "description": "Time the End-User's information was last updated. Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time.", + "format": "int64", + "type": "integer" + }, + "website": { + "description": "URL of the End-User's Web page or blog. This Web page SHOULD contain information published by the End-User or an organization that the End-User is affiliated with.", + "type": "string" + }, + "zoneinfo": { + "description": "String from zoneinfo [zoneinfo] time zone database representing the End-User's time zone. For example, Europe/Paris or America/Los_Angeles.", + "type": "string" + } + }, + "type": "object" + }, + "pagination": { + "properties": { + "page_size": { + "default": 250, + "description": "Items per page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "format": "int64", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "page_token": { + "default": "1", + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "minimum": 1, + "type": "string" + } + }, + "type": "object" + }, + "paginationHeaders": { + "properties": { + "link": { + "description": "The link header contains pagination links.\n\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\nin: header", + "type": "string" + }, + "x-total-count": { + "description": "The total number of clients.\n\nin: header", + "type": "string" + } + }, + "type": "object" + }, + "rejectOAuth2Request": { + "properties": { + "error": { + "description": "The error should follow the OAuth2 error format (e.g. `invalid_request`, `login_required`).\n\nDefaults to `request_denied`.", + "type": "string" + }, + "error_debug": { + "description": "Debug contains information to help resolve the problem as a developer. Usually not exposed\nto the public but only in the server logs.", + "type": "string" + }, + "error_description": { + "description": "Description of the error in a human readable format.", + "type": "string" + }, + "error_hint": { + "description": "Hint to help resolve the error.", + "type": "string" + }, + "status_code": { + "description": "Represents the HTTP status code of the error (e.g. 401 or 403)\n\nDefaults to 400", + "format": "int64", + "type": "integer" + } + }, + "title": "The request payload used to accept a login or consent request.", + "type": "object" + }, + "tokenPagination": { + "properties": { + "page_size": { + "default": 250, + "description": "Items per page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "format": "int64", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "page_token": { + "default": "1", + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "minimum": 1, + "type": "string" + } + }, + "type": "object" + }, + "tokenPaginationHeaders": { + "properties": { + "link": { + "description": "The link header contains pagination links.\n\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\nin: header", + "type": "string" + }, + "x-total-count": { + "description": "The total number of clients.\n\nin: header", + "type": "string" + } + }, + "type": "object" + }, + "tokenPaginationRequestParameters": { + "description": "The `Link` HTTP header contains multiple links (`first`, `next`, `last`, `previous`) formatted as:\n`; rel=\"{page}\"`\n\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "properties": { + "page_size": { + "default": 250, + "description": "Items per Page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "format": "int64", + "maximum": 500, + "minimum": 1, + "type": "integer" + }, + "page_token": { + "default": "1", + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "minimum": 1, + "type": "string" + } + }, + "title": "Pagination Request Parameters", + "type": "object" + }, + "tokenPaginationResponseHeaders": { + "description": "The `Link` HTTP header contains multiple links (`first`, `next`, `last`, `previous`) formatted as:\n`; rel=\"{page}\"`\n\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "properties": { + "link": { + "description": "The Link HTTP Header\n\nThe `Link` header contains a comma-delimited list of links to the following pages:\n\nfirst: The first page of results.\nnext: The next page of results.\nprev: The previous page of results.\nlast: The last page of results.\n\nPages are omitted if they do not exist. For example, if there is no next page, the `next` link is omitted. Examples:\n\n; rel=\"first\",; rel=\"next\",; rel=\"prev\",; rel=\"last\"", + "type": "string" + }, + "x-total-count": { + "description": "The X-Total-Count HTTP Header\n\nThe `X-Total-Count` header contains the total number of items in the collection.", + "format": "int64", + "type": "integer" + } + }, + "title": "Pagination Response Header", + "type": "object" + }, + "trustOAuth2JwtGrantIssuer": { + "description": "Trust OAuth2 JWT Bearer Grant Type Issuer Request Body", + "properties": { + "allow_any_subject": { + "description": "The \"allow_any_subject\" indicates that the issuer is allowed to have any principal as the subject of the JWT.", + "type": "boolean" + }, + "expires_at": { + "description": "The \"expires_at\" indicates, when grant will expire, so we will reject assertion from \"issuer\" targeting \"subject\".", + "format": "date-time", + "type": "string" + }, + "issuer": { + "description": "The \"issuer\" identifies the principal that issued the JWT assertion (same as \"iss\" claim in JWT).", + "example": "https://jwt-idp.example.com", + "type": "string" + }, + "jwk": { + "$ref": "#/components/schemas/jsonWebKey" + }, + "scope": { + "description": "The \"scope\" contains list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749])", + "example": [ + "openid", + "offline" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "subject": { + "description": "The \"subject\" identifies the principal that is the subject of the JWT.", + "example": "mike@example.com", + "type": "string" + } + }, + "required": [ + "issuer", + "scope", + "jwk", + "expires_at" + ], + "type": "object" + }, + "trustedOAuth2JwtGrantIssuer": { + "description": "OAuth2 JWT Bearer Grant Type Issuer Trust Relationship", + "properties": { + "allow_any_subject": { + "description": "The \"allow_any_subject\" indicates that the issuer is allowed to have any principal as the subject of the JWT.", + "type": "boolean" + }, + "created_at": { + "description": "The \"created_at\" indicates, when grant was created.", + "format": "date-time", + "type": "string" + }, + "expires_at": { + "description": "The \"expires_at\" indicates, when grant will expire, so we will reject assertion from \"issuer\" targeting \"subject\".", + "format": "date-time", + "type": "string" + }, + "id": { + "example": "9edc811f-4e28-453c-9b46-4de65f00217f", + "type": "string" + }, + "issuer": { + "description": "The \"issuer\" identifies the principal that issued the JWT assertion (same as \"iss\" claim in JWT).", + "example": "https://jwt-idp.example.com", + "type": "string" + }, + "public_key": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantJsonWebKey" + }, + "scope": { + "description": "The \"scope\" contains list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749])", + "example": [ + "openid", + "offline" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "subject": { + "description": "The \"subject\" identifies the principal that is the subject of the JWT.", + "example": "mike@example.com", + "type": "string" + } + }, + "type": "object" + }, + "trustedOAuth2JwtGrantIssuers": { + "description": "OAuth2 JWT Bearer Grant Type Issuer Trust Relationships", + "items": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantIssuer" + }, + "type": "array" + }, + "trustedOAuth2JwtGrantJsonWebKey": { + "description": "OAuth2 JWT Bearer Grant Type Issuer Trusted JSON Web Key", + "properties": { + "kid": { + "description": "The \"key_id\" is key unique identifier (same as kid header in jws/jwt).", + "example": "123e4567-e89b-12d3-a456-426655440000", + "type": "string" + }, + "set": { + "description": "The \"set\" is basically a name for a group(set) of keys. Will be the same as \"issuer\" in grant.", + "example": "https://jwt-idp.example.com", + "type": "string" + } + }, + "type": "object" + }, + "unexpectedError": { + "type": "string" + }, + "version": { + "properties": { + "version": { + "description": "Version is the service's version.", + "type": "string" + } + }, + "type": "object" + } + }, + "securitySchemes": { + "basic": { + "scheme": "basic", + "type": "http" + }, + "bearer": { + "scheme": "bearer", + "type": "http" + }, + "oauth2": { + "flows": { + "authorizationCode": { + "authorizationUrl": "https://hydra.demo.ory.sh/oauth2/auth", + "scopes": { + "offline": "A scope required when requesting refresh tokens (alias for `offline_access`)", + "offline_access": "A scope required when requesting refresh tokens", + "openid": "Request an OpenID Connect ID Token" + }, + "tokenUrl": "https://hydra.demo.ory.sh/oauth2/token" + } + }, + "type": "oauth2" + } + } + }, + "info": { + "contact": { + "email": "hi@ory.sh" + }, + "description": "Documentation for all of Ory Hydra's APIs.\n", + "license": { + "name": "Apache 2.0" + }, + "title": "Ory Hydra API", + "version": "" + }, + "openapi": "3.0.3", + "paths": { + "/.well-known/jwks.json": { + "get": { + "description": "This endpoint returns JSON Web Keys required to verifying OpenID Connect ID Tokens and,\nif enabled, OAuth 2.0 JWT Access Tokens. This endpoint can be used with client libraries like\n[node-jwks-rsa](https://github.com/auth0/node-jwks-rsa) among others.", + "operationId": "discoverJsonWebKeys", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Discover Well-Known JSON Web Keys", + "tags": [ + "wellknown" + ] + } + }, + "/.well-known/openid-configuration": { + "get": { + "description": "A mechanism for an OpenID Connect Relying Party to discover the End-User's OpenID Provider and obtain information needed to interact with it, including its OAuth 2.0 endpoint locations.\n\nPopular libraries for OpenID Connect clients include oidc-client-js (JavaScript), go-oidc (Golang), and others.\nFor a full list of clients go here: https://openid.net/developers/certified/", + "operationId": "discoverOidcConfiguration", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oidcConfiguration" + } + } + }, + "description": "oidcConfiguration" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "OpenID Connect Discovery", + "tags": [ + "oidc" + ] + } + }, + "/admin/clients": { + "get": { + "description": "This endpoint lists all clients in the database, and never returns client secrets.\nAs a default it lists the first 100 clients.", + "operationId": "listOAuth2Clients", + "parameters": [ + { + "description": "Items per Page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "in": "query", + "name": "page_size", + "schema": { + "default": 250, + "format": "int64", + "maximum": 500, + "minimum": 1, + "type": "integer" + } + }, + { + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "in": "query", + "name": "page_token", + "schema": { + "default": "1", + "minimum": 1, + "type": "string" + } + }, + { + "description": "The name of the clients to filter by.", + "in": "query", + "name": "client_name", + "schema": { + "type": "string" + } + }, + { + "description": "The owner of the clients to filter by.", + "in": "query", + "name": "owner", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/listOAuth2Clients" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "List OAuth 2.0 Clients", + "tags": [ + "oAuth2" + ] + }, + "post": { + "description": "Create a new OAuth 2.0 client. If you pass `client_secret` the secret is used, otherwise a random secret\nis generated. The secret is echoed in the response. It is not possible to retrieve it later on.", + "operationId": "createOAuth2Client", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "OAuth 2.0 Client Request Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "400": { + "$ref": "#/components/responses/errorOAuth2BadRequest" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Create OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/clients/{id}": { + "delete": { + "description": "Delete an existing OAuth 2.0 Client by its ID.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.\n\nMake sure that this endpoint is well protected and only callable by first-party components.", + "operationId": "deleteOAuth2Client", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Delete OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + }, + "get": { + "description": "Get an OAuth 2.0 client by its ID. This endpoint never returns the client secret.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "getOAuth2Client", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Get an OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + }, + "patch": { + "description": "Patch an existing OAuth 2.0 Client using JSON Patch. If you pass `client_secret`\nthe secret will be updated and returned via the API. This is the\nonly time you will be able to retrieve the client secret, so write it down and keep it safe.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "patchOAuth2Client", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonPatchDocument" + } + } + }, + "description": "OAuth 2.0 Client JSON Patch Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "404": { + "$ref": "#/components/responses/errorOAuth2NotFound" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Patch OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + }, + "put": { + "description": "Replaces an existing OAuth 2.0 Client with the payload you send. If you pass `client_secret` the secret is used,\notherwise the existing secret is used.\n\nIf set, the secret is echoed in the response. It is not possible to retrieve it later on.\n\nOAuth 2.0 Clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "setOAuth2Client", + "parameters": [ + { + "description": "OAuth 2.0 Client ID", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "OAuth 2.0 Client Request Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "400": { + "$ref": "#/components/responses/errorOAuth2BadRequest" + }, + "404": { + "$ref": "#/components/responses/errorOAuth2NotFound" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Set OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/clients/{id}/lifespans": { + "put": { + "description": "Set lifespans of different token types issued for this OAuth 2.0 client. Does not modify other fields.", + "operationId": "setOAuth2ClientLifespans", + "parameters": [ + { + "description": "OAuth 2.0 Client ID", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2ClientTokenLifespans" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Set OAuth2 Client Token Lifespans", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/keys/{set}": { + "delete": { + "description": "Use this endpoint to delete a complete JSON Web Key Set and all the keys in that set.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "deleteJsonWebKeySet", + "parameters": [ + { + "description": "The JSON Web Key Set", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Delete JSON Web Key Set", + "tags": [ + "jwk" + ] + }, + "get": { + "description": "This endpoint can be used to retrieve JWK Sets stored in ORY Hydra.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "getJsonWebKeySet", + "parameters": [ + { + "description": "JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Retrieve a JSON Web Key Set", + "tags": [ + "jwk" + ] + }, + "post": { + "description": "This endpoint is capable of generating JSON Web Key Sets for you. There a different strategies available, such as symmetric cryptographic keys (HS256, HS512) and asymetric cryptographic keys (RS256, ECDSA). If the specified JSON Web Key Set does not exist, it will be created.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "createJsonWebKeySet", + "parameters": [ + { + "description": "The JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createJsonWebKeySet" + } + } + }, + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Create JSON Web Key", + "tags": [ + "jwk" + ] + }, + "put": { + "description": "Use this method if you do not want to let Hydra generate the JWKs for you, but instead save your own.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "setJsonWebKeySet", + "parameters": [ + { + "description": "The JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Update a JSON Web Key Set", + "tags": [ + "jwk" + ] + } + }, + "/admin/keys/{set}/{kid}": { + "delete": { + "description": "Use this endpoint to delete a single JSON Web Key.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A\nJWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses\nthis functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens),\nand allows storing user-defined keys as well.", + "operationId": "deleteJsonWebKey", + "parameters": [ + { + "description": "The JSON Web Key Set", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The JSON Web Key ID (kid)", + "in": "path", + "name": "kid", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Delete JSON Web Key", + "tags": [ + "jwk" + ] + }, + "get": { + "description": "This endpoint returns a singular JSON Web Key contained in a set. It is identified by the set and the specific key ID (kid).", + "operationId": "getJsonWebKey", + "parameters": [ + { + "description": "JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "JSON Web Key ID", + "in": "path", + "name": "kid", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Get JSON Web Key", + "tags": [ + "jwk" + ] + }, + "put": { + "description": "Use this method if you do not want to let Hydra generate the JWKs for you, but instead save your own.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "setJsonWebKey", + "parameters": [ + { + "description": "The JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "JSON Web Key ID", + "in": "path", + "name": "kid", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKey" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKey" + } + } + }, + "description": "jsonWebKey" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Set JSON Web Key", + "tags": [ + "jwk" + ] + } + }, + "/admin/oauth2/auth/requests/consent": { + "get": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell Ory now about it. If the subject authenticated, he/she must now be asked if\nthe OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the subject's behalf.\n\nThe consent challenge is appended to the consent provider's URL to which the subject's user-agent (browser) is redirected to. The consent\nprovider uses that challenge to fetch information on the OAuth2 request and then tells Ory if the subject accepted\nor rejected the request.\n\nThe default consent provider is available via the Ory Managed Account Experience. To customize the consent provider, please\nhead over to the OAuth 2.0 documentation.", + "operationId": "getOAuth2ConsentRequest", + "parameters": [ + { + "description": "OAuth 2.0 Consent Request Challenge", + "in": "query", + "name": "consent_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2ConsentRequest" + } + } + }, + "description": "oAuth2ConsentRequest" + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Get OAuth 2.0 Consent Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/consent/accept": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell Ory now about it. If the subject authenticated, he/she must now be asked if\nthe OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the subject's behalf.\n\nThe consent challenge is appended to the consent provider's URL to which the subject's user-agent (browser) is redirected to. The consent\nprovider uses that challenge to fetch information on the OAuth2 request and then tells Ory if the subject accepted\nor rejected the request.\n\nThis endpoint tells Ory that the subject has authorized the OAuth 2.0 client to access resources on his/her behalf.\nThe consent provider includes additional information, such as session data for access and ID tokens, and if the\nconsent request should be used as basis for future requests.\n\nThe response contains a redirect URL which the consent provider should redirect the user-agent to.\n\nThe default consent provider is available via the Ory Managed Account Experience. To customize the consent provider, please\nhead over to the OAuth 2.0 documentation.", + "operationId": "acceptOAuth2ConsentRequest", + "parameters": [ + { + "description": "OAuth 2.0 Consent Request Challenge", + "in": "query", + "name": "consent_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/acceptOAuth2ConsentRequest" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Accept OAuth 2.0 Consent Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/consent/reject": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell Ory now about it. If the subject authenticated, he/she must now be asked if\nthe OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the subject's behalf.\n\nThe consent challenge is appended to the consent provider's URL to which the subject's user-agent (browser) is redirected to. The consent\nprovider uses that challenge to fetch information on the OAuth2 request and then tells Ory if the subject accepted\nor rejected the request.\n\nThis endpoint tells Ory that the subject has not authorized the OAuth 2.0 client to access resources on his/her behalf.\nThe consent provider must include a reason why the consent was not granted.\n\nThe response contains a redirect URL which the consent provider should redirect the user-agent to.\n\nThe default consent provider is available via the Ory Managed Account Experience. To customize the consent provider, please\nhead over to the OAuth 2.0 documentation.", + "operationId": "rejectOAuth2ConsentRequest", + "parameters": [ + { + "description": "OAuth 2.0 Consent Request Challenge", + "in": "query", + "name": "consent_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rejectOAuth2Request" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Reject OAuth 2.0 Consent Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/login": { + "get": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell the Ory OAuth2 Service about it.\n\nPer default, the login provider is Ory itself. You may use a different login provider which needs to be a web-app\nyou write and host, and it must be able to authenticate (\"show the subject a login screen\")\na subject (in OAuth2 the proper name for subject is \"resource owner\").\n\nThe authentication challenge is appended to the login provider URL to which the subject's user-agent (browser) is redirected to. The login\nprovider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process.", + "operationId": "getOAuth2LoginRequest", + "parameters": [ + { + "description": "OAuth 2.0 Login Request Challenge", + "in": "query", + "name": "login_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2LoginRequest" + } + } + }, + "description": "oAuth2LoginRequest" + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Get OAuth 2.0 Login Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/login/accept": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell the Ory OAuth2 Service about it.\n\nThe authentication challenge is appended to the login provider URL to which the subject's user-agent (browser) is redirected to. The login\nprovider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process.\n\nThis endpoint tells Ory that the subject has successfully authenticated and includes additional information such as\nthe subject's ID and if Ory should remember the subject's subject agent for future authentication attempts by setting\na cookie.\n\nThe response contains a redirect URL which the login provider should redirect the user-agent to.", + "operationId": "acceptOAuth2LoginRequest", + "parameters": [ + { + "description": "OAuth 2.0 Login Request Challenge", + "in": "query", + "name": "login_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/acceptOAuth2LoginRequest" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Accept OAuth 2.0 Login Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/login/reject": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell the Ory OAuth2 Service about it.\n\nThe authentication challenge is appended to the login provider URL to which the subject's user-agent (browser) is redirected to. The login\nprovider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process.\n\nThis endpoint tells Ory that the subject has not authenticated and includes a reason why the authentication\nwas denied.\n\nThe response contains a redirect URL which the login provider should redirect the user-agent to.", + "operationId": "rejectOAuth2LoginRequest", + "parameters": [ + { + "description": "OAuth 2.0 Login Request Challenge", + "in": "query", + "name": "login_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rejectOAuth2Request" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Reject OAuth 2.0 Login Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/logout": { + "get": { + "description": "Use this endpoint to fetch an Ory OAuth 2.0 logout request.", + "operationId": "getOAuth2LogoutRequest", + "parameters": [ + { + "in": "query", + "name": "logout_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2LogoutRequest" + } + } + }, + "description": "oAuth2LogoutRequest" + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Get OAuth 2.0 Session Logout Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/logout/accept": { + "put": { + "description": "When a user or an application requests Ory OAuth 2.0 to remove the session state of a subject, this endpoint is used to confirm that logout request.\n\nThe response contains a redirect URL which the consent provider should redirect the user-agent to.", + "operationId": "acceptOAuth2LogoutRequest", + "parameters": [ + { + "description": "OAuth 2.0 Logout Request Challenge", + "in": "query", + "name": "logout_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Accept OAuth 2.0 Session Logout Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/logout/reject": { + "put": { + "description": "When a user or an application requests Ory OAuth 2.0 to remove the session state of a subject, this endpoint is used to deny that logout request.\nNo HTTP request body is required.\n\nThe response is empty as the logout provider has to chose what action to perform next.", + "operationId": "rejectOAuth2LogoutRequest", + "parameters": [ + { + "in": "query", + "name": "logout_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Reject OAuth 2.0 Session Logout Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/sessions/consent": { + "delete": { + "description": "This endpoint revokes a subject's granted consent sessions and invalidates all\nassociated OAuth 2.0 Access Tokens. You may also only revoke sessions for a specific OAuth 2.0 Client ID.", + "operationId": "revokeOAuth2ConsentSessions", + "parameters": [ + { + "description": "OAuth 2.0 Consent Subject\n\nThe subject whose consent sessions should be deleted.", + "in": "query", + "name": "subject", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "OAuth 2.0 Client ID\n\nIf set, deletes only those consent sessions that have been granted to the specified OAuth 2.0 Client ID.", + "in": "query", + "name": "client", + "schema": { + "type": "string" + } + }, + { + "description": "Revoke All Consent Sessions\n\nIf set to `true` deletes all consent sessions by the Subject that have been granted.", + "in": "query", + "name": "all", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Revoke OAuth 2.0 Consent Sessions of a Subject", + "tags": [ + "oAuth2" + ] + }, + "get": { + "description": "This endpoint lists all subject's granted consent sessions, including client and granted scope.\nIf the subject is unknown or has not granted any consent sessions yet, the endpoint returns an\nempty JSON array with status code 200 OK.", + "operationId": "listOAuth2ConsentSessions", + "parameters": [ + { + "description": "Items per Page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "in": "query", + "name": "page_size", + "schema": { + "default": 250, + "format": "int64", + "maximum": 500, + "minimum": 1, + "type": "integer" + } + }, + { + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "in": "query", + "name": "page_token", + "schema": { + "default": "1", + "minimum": 1, + "type": "string" + } + }, + { + "description": "The subject to list the consent sessions for.", + "in": "query", + "name": "subject", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The login session id to list the consent sessions for.", + "in": "query", + "name": "login_session_id", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2ConsentSessions" + } + } + }, + "description": "oAuth2ConsentSessions" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "List OAuth 2.0 Consent Sessions of a Subject", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/sessions/login": { + "delete": { + "description": "This endpoint invalidates authentication sessions. After revoking the authentication session(s), the subject\nhas to re-authenticate at the Ory OAuth2 Provider. This endpoint does not invalidate any tokens.\n\nIf you send the subject in a query param, all authentication sessions that belong to that subject are revoked.\nNo OpennID Connect Front- or Back-channel logout is performed in this case.\n\nAlternatively, you can send a SessionID via `sid` query param, in which case, only the session that is connected\nto that SessionID is revoked. OpenID Connect Back-channel logout is performed in this case.", + "operationId": "revokeOAuth2LoginSessions", + "parameters": [ + { + "description": "OAuth 2.0 Subject\n\nThe subject to revoke authentication sessions for.", + "in": "query", + "name": "subject", + "schema": { + "type": "string" + } + }, + { + "description": "OAuth 2.0 Subject\n\nThe subject to revoke authentication sessions for.", + "in": "query", + "name": "sid", + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Revokes OAuth 2.0 Login Sessions by either a Subject or a SessionID", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/introspect": { + "post": { + "description": "The introspection endpoint allows to check if a token (both refresh and access) is active or not. An active token\nis neither expired nor revoked. If a token is active, additional information on the token will be included. You can\nset additional data for a token by setting `session.access_token` during the consent flow.", + "operationId": "introspectOAuth2Token", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "properties": { + "scope": { + "description": "An optional, space separated list of required scopes. If the access token was not granted one of the\nscopes, the result of active will be false.", + "type": "string", + "x-formData-name": "scope" + }, + "token": { + "description": "The string value of the token. For access tokens, this\nis the \"access_token\" value returned from the token endpoint\ndefined in OAuth 2.0. For refresh tokens, this is the \"refresh_token\"\nvalue returned.", + "required": [ + "token" + ], + "type": "string", + "x-formData-name": "token" + } + }, + "required": [ + "token" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/introspectedOAuth2Token" + } + } + }, + "description": "introspectedOAuth2Token" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Introspect OAuth2 Access and Refresh Tokens", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/tokens": { + "delete": { + "description": "This endpoint deletes OAuth2 access tokens issued to an OAuth 2.0 Client from the database.", + "operationId": "deleteOAuth2Token", + "parameters": [ + { + "description": "OAuth 2.0 Client ID", + "in": "query", + "name": "client_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Delete OAuth 2.0 Access Tokens from specific OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/trust/grants/jwt-bearer/issuers": { + "get": { + "description": "Use this endpoint to list all trusted JWT Bearer Grant Type Issuers.", + "operationId": "listTrustedOAuth2JwtGrantIssuers", + "parameters": [ + { + "in": "query", + "name": "MaxItems", + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "in": "query", + "name": "DefaultItems", + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "If optional \"issuer\" is supplied, only jwt-bearer grants with this issuer will be returned.", + "in": "query", + "name": "issuer", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantIssuers" + } + } + }, + "description": "trustedOAuth2JwtGrantIssuers" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "List Trusted OAuth2 JWT Bearer Grant Type Issuers", + "tags": [ + "oAuth2" + ] + }, + "post": { + "description": "Use this endpoint to establish a trust relationship for a JWT issuer\nto perform JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication\nand Authorization Grants [RFC7523](https://datatracker.ietf.org/doc/html/rfc7523).", + "operationId": "trustOAuth2JwtGrantIssuer", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/trustOAuth2JwtGrantIssuer" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantIssuer" + } + } + }, + "description": "trustedOAuth2JwtGrantIssuer" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Trust OAuth2 JWT Bearer Grant Type Issuer", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/trust/grants/jwt-bearer/issuers/{id}": { + "delete": { + "description": "Use this endpoint to delete trusted JWT Bearer Grant Type Issuer. The ID is the one returned when you\ncreated the trust relationship.\n\nOnce deleted, the associated issuer will no longer be able to perform the JSON Web Token (JWT) Profile\nfor OAuth 2.0 Client Authentication and Authorization Grant.", + "operationId": "deleteTrustedOAuth2JwtGrantIssuer", + "parameters": [ + { + "description": "The id of the desired grant", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Delete Trusted OAuth2 JWT Bearer Grant Type Issuer", + "tags": [ + "oAuth2" + ] + }, + "get": { + "description": "Use this endpoint to get a trusted JWT Bearer Grant Type Issuer. The ID is the one returned when you\ncreated the trust relationship.", + "operationId": "getTrustedOAuth2JwtGrantIssuer", + "parameters": [ + { + "description": "The id of the desired grant", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantIssuer" + } + } + }, + "description": "trustedOAuth2JwtGrantIssuer" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Get Trusted OAuth2 JWT Bearer Grant Type Issuer", + "tags": [ + "oAuth2" + ] + } + }, + "/health/alive": { + "get": { + "description": "This endpoint returns a HTTP 200 status code when Ory Hydra is accepting incoming\nHTTP requests. This status does currently not include checks whether the database connection is working.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the health status will never\nrefer to the cluster state, only to a single instance.", + "operationId": "isAlive", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/healthStatus" + } + } + }, + "description": "Ory Hydra is ready to accept connections." + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Check HTTP Server Status", + "tags": [ + "metadata" + ] + } + }, + "/health/ready": { + "get": { + "description": "This endpoint returns a HTTP 200 status code when Ory Hydra is up running and the environment dependencies (e.g.\nthe database) are responsive as well.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of Ory Hydra, the health status will never\nrefer to the cluster state, only to a single instance.", + "operationId": "isReady", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "description": "Always \"ok\".", + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "Ory Hydra is ready to accept requests." + }, + "503": { + "content": { + "application/json": { + "schema": { + "properties": { + "errors": { + "additionalProperties": { + "type": "string" + }, + "description": "Errors contains a list of errors that caused the not ready status.", + "type": "object" + } + }, + "type": "object" + } + } + }, + "description": "Ory Kratos is not yet ready to accept requests." + } + }, + "summary": "Check HTTP Server and Database Status", + "tags": [ + "metadata" + ] + } + }, + "/oauth2/auth": { + "get": { + "description": "Use open source libraries to perform OAuth 2.0 and OpenID Connect\navailable for any programming language. You can find a list of libraries at https://oauth.net/code/\n\nThe Ory SDK is not yet able to this endpoint properly.", + "operationId": "oAuth2Authorize", + "responses": { + "302": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "OAuth 2.0 Authorize Endpoint", + "tags": [ + "oAuth2" + ] + } + }, + "/oauth2/register": { + "post": { + "description": "This endpoint behaves like the administrative counterpart (`createOAuth2Client`) but is capable of facing the\npublic internet directly and can be used in self-service. It implements the OpenID Connect\nDynamic Client Registration Protocol. This feature needs to be enabled in the configuration. This endpoint\nis disabled by default. It can be enabled by an administrator.\n\nPlease note that using this endpoint you are not able to choose the `client_secret` nor the `client_id` as those\nvalues will be server generated when specifying `token_endpoint_auth_method` as `client_secret_basic` or\n`client_secret_post`.\n\nThe `client_secret` will be returned in the response and you will not be able to retrieve it later on.\nWrite the secret down and keep it somewhere safe.", + "operationId": "createOidcDynamicClient", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "Dynamic Client Registration Request Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "400": { + "$ref": "#/components/responses/errorOAuth2BadRequest" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Register OAuth2 Client using OpenID Dynamic Client Registration", + "tags": [ + "oidc" + ] + } + }, + "/oauth2/register/{id}": { + "delete": { + "description": "This endpoint behaves like the administrative counterpart (`deleteOAuth2Client`) but is capable of facing the\npublic internet directly and can be used in self-service. It implements the OpenID Connect\nDynamic Client Registration Protocol. This feature needs to be enabled in the configuration. This endpoint\nis disabled by default. It can be enabled by an administrator.\n\nTo use this endpoint, you will need to present the client's authentication credentials. If the OAuth2 Client\nuses the Token Endpoint Authentication Method `client_secret_post`, you need to present the client secret in the URL query.\nIf it uses `client_secret_basic`, present the Client ID and the Client Secret in the Authorization header.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "deleteOidcDynamicClient", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Delete OAuth 2.0 Client using the OpenID Dynamic Client Registration Management Protocol", + "tags": [ + "oidc" + ] + }, + "get": { + "description": "This endpoint behaves like the administrative counterpart (`getOAuth2Client`) but is capable of facing the\npublic internet directly and can be used in self-service. It implements the OpenID Connect\nDynamic Client Registration Protocol.\n\nTo use this endpoint, you will need to present the client's authentication credentials. If the OAuth2 Client\nuses the Token Endpoint Authentication Method `client_secret_post`, you need to present the client secret in the URL query.\nIf it uses `client_secret_basic`, present the Client ID and the Client Secret in the Authorization header.", + "operationId": "getOidcDynamicClient", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Get OAuth2 Client using OpenID Dynamic Client Registration", + "tags": [ + "oidc" + ] + }, + "put": { + "description": "This endpoint behaves like the administrative counterpart (`setOAuth2Client`) but is capable of facing the\npublic internet directly to be used by third parties. It implements the OpenID Connect\nDynamic Client Registration Protocol.\n\nThis feature is disabled per default. It can be enabled by a system administrator.\n\nIf you pass `client_secret` the secret is used, otherwise the existing secret is used. If set, the secret is echoed in the response.\nIt is not possible to retrieve it later on.\n\nTo use this endpoint, you will need to present the client's authentication credentials. If the OAuth2 Client\nuses the Token Endpoint Authentication Method `client_secret_post`, you need to present the client secret in the URL query.\nIf it uses `client_secret_basic`, present the Client ID and the Client Secret in the Authorization header.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "setOidcDynamicClient", + "parameters": [ + { + "description": "OAuth 2.0 Client ID", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "OAuth 2.0 Client Request Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "404": { + "$ref": "#/components/responses/errorOAuth2NotFound" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Set OAuth2 Client using OpenID Dynamic Client Registration", + "tags": [ + "oidc" + ] + } + }, + "/oauth2/revoke": { + "post": { + "description": "Revoking a token (both access and refresh) means that the tokens will be invalid. A revoked access token can no\nlonger be used to make access requests, and a revoked refresh token can no longer be used to refresh an access token.\nRevoking a refresh token also invalidates the access token that was created with it. A token may only be revoked by\nthe client the token was generated for.", + "operationId": "revokeOAuth2Token", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "properties": { + "client_id": { + "type": "string", + "x-formData-name": "client_id" + }, + "client_secret": { + "type": "string", + "x-formData-name": "client_secret" + }, + "token": { + "required": [ + "token" + ], + "type": "string", + "x-formData-name": "token" + } + }, + "required": [ + "token" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "security": [ + { + "basic": [] + }, + { + "oauth2": [] + } + ], + "summary": "Revoke OAuth 2.0 Access or Refresh Token", + "tags": [ + "oAuth2" + ] + } + }, + "/oauth2/sessions/logout": { + "get": { + "description": "This endpoint initiates and completes user logout at the Ory OAuth2 & OpenID provider and initiates OpenID Connect Front- / Back-channel logout:\n\nhttps://openid.net/specs/openid-connect-frontchannel-1_0.html\nhttps://openid.net/specs/openid-connect-backchannel-1_0.html\n\nBack-channel logout is performed asynchronously and does not affect logout flow.", + "operationId": "revokeOidcSession", + "responses": { + "302": { + "$ref": "#/components/responses/emptyResponse" + } + }, + "summary": "OpenID Connect Front- and Back-channel Enabled Logout", + "tags": [ + "oidc" + ] + } + }, + "/oauth2/token": { + "post": { + "description": "Use open source libraries to perform OAuth 2.0 and OpenID Connect\navailable for any programming language. You can find a list of libraries here https://oauth.net/code/\n\nThe Ory SDK is not yet able to this endpoint properly.", + "operationId": "oauth2TokenExchange", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "properties": { + "client_id": { + "type": "string", + "x-formData-name": "client_id" + }, + "code": { + "type": "string", + "x-formData-name": "code" + }, + "grant_type": { + "required": [ + "grant_type" + ], + "type": "string", + "x-formData-name": "grant_type" + }, + "redirect_uri": { + "type": "string", + "x-formData-name": "redirect_uri" + }, + "refresh_token": { + "type": "string", + "x-formData-name": "refresh_token" + } + }, + "required": [ + "grant_type" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2TokenExchange" + } + } + }, + "description": "oAuth2TokenExchange" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "security": [ + { + "basic": [] + }, + { + "oauth2": [] + } + ], + "summary": "The OAuth 2.0 Token Endpoint", + "tags": [ + "oAuth2" + ] + } + }, + "/userinfo": { + "get": { + "description": "This endpoint returns the payload of the ID Token, including `session.id_token` values, of\nthe provided OAuth 2.0 Access Token's consent request.\n\nIn the case of authentication error, a WWW-Authenticate header might be set in the response\nwith more information about the error. See [the spec](https://datatracker.ietf.org/doc/html/rfc6750#section-3)\nfor more details about header format.", + "operationId": "getOidcUserInfo", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oidcUserInfo" + } + } + }, + "description": "oidcUserInfo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "security": [ + { + "oauth2": [] + } + ], + "summary": "OpenID Connect Userinfo", + "tags": [ + "oidc" + ] + } + }, + "/version": { + "get": { + "description": "This endpoint returns the version of Ory Hydra.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the version will never\nrefer to the cluster state, only to a single instance.", + "operationId": "getVersion", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "version": { + "description": "The version of Ory Hydra.", + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "Returns the Ory Hydra version." + } + }, + "summary": "Return Running Software Version.", + "tags": [ + "metadata" + ] + } + } + }, + "tags": [ + { + "description": "OAuth 2.0", + "name": "oAuth2" + }, + { + "description": "OpenID Connect", + "name": "oidc" + }, + { + "description": "JSON Web Keys", + "name": "jwk" + }, + { + "description": "Well-Known Endpoints", + "name": "wellknown" + }, + { + "description": "Service Metadata", + "name": "metadata" + } + ], + "x-forwarded-proto": "string", + "x-request-id": "string" +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/test/resources/resource-server-open-api-v3.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/test/resources/resource-server-open-api-v3.json new file mode 100644 index 0000000..d475800 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/src/test/resources/resource-server-open-api-v3.json @@ -0,0 +1,115 @@ +{ + "openapi": "3.0.1", + "info": { + "description": "Example resource server" + }, + "servers": [ + { + "url": "https://resource-server.com/api", + "description": "Generated server url" + } + ], + "security": [ + { + "Introspect OAuth 2.0": [] + } + ], + "paths": { + "/calculate": { + "put": { + "tags": [ + "calculate-controller" + ], + "summary": "Some calculate", + "operationId": "calculate", + "responses": { + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "500": { + "description": "Internal Server Error" + }, + "200": { + "description": "OK" + } + } + } + }, + "/statistics": { + "get": { + "tags": [ + "statistics-controller" + ], + "summary": "Get statistics", + "operationId": "getStatistics", + "responses": { + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "500": { + "description": "Internal Server Error" + }, + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Point" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Point": { + "type": "object", + "properties": { + "x": { + "type": "number", + "format": "double" + }, + "y": { + "type": "number", + "format": "double" + } + }, + "description": "Graph point" + } + }, + "securitySchemes": { + "Introspect OAuth 2.0": { + "type": "http", + "in": "header", + "scheme": "bearer", + "bearerFormat": "opaque token" + } + } + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/test_and_report.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/test_and_report.bash new file mode 100644 index 0000000..ef6d5fe --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-backend/test_and_report.bash @@ -0,0 +1,3 @@ +#!/bin/bash + +./gradlew test jacocoTestReport diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/.editorconfig b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/.editorconfig new file mode 100644 index 0000000..59d9a3a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/.gitignore b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/.gitignore new file mode 100644 index 0000000..1f4031f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/.gitignore @@ -0,0 +1,44 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings + +# System files +.DS_Store +Thumbs.db + +package-lock.json diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/angular.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/angular.json new file mode 100644 index 0000000..5565650 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/angular.json @@ -0,0 +1,121 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "readonly-frontend": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:application", + "options": { + "outputPath": "dist/readonly-frontend", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": [ + "zone.js" + ], + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "src/favicon.ico", + "src/assets", + "src/fonts" + ], + "styles": [ + "src/styles.scss" + ], + "scripts": [], + "server": "src/main.server.ts", + "prerender": true, + "ssr": { + "entry": "server.ts" + } + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "readonly-frontend:build:production" + }, + "development": { + "buildTarget": "readonly-frontend:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "readonly-frontend:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "polyfills": [ + "zone.js", + "zone.js/testing" + ], + "tsConfig": "tsconfig.spec.json", + "inlineStyleLanguage": "scss", + "assets": [ + "src/favicon.ico", + "src/assets", + "src/fonts" + ], + "styles": [ + "src/styles.scss" + ], + "scripts": [], + "karmaConfig": "karma.conf.js", + "codeCoverageExclude": [ + "src/app/window.service.ts" + ] + } + } + } + } + }, + "cli": { + "analytics": false + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/build_image.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/build_image.bash new file mode 100644 index 0000000..fb801f0 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/build_image.bash @@ -0,0 +1,47 @@ +#!/bin/bash + +# export HTTP_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +# export HTTPS_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +# export NO_PROXY="localhost,127.0.0.1" + +export REPO_IMAGE="chistousov" +export PROJECT_NAME="ory-hydra-oauth2-example-client-readonly-frontend" +export VERSION="1.0.0" + +# install pack +# https://buildpacks.io/docs/tools/pack/#linux-script-install +# (curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.29.0/pack-v0.29.0-linux.tgz" | sudo tar -C /usr/local/bin/ --no-same-owner -xzv pack) + +npm i + +ng test --no-watch --code-coverage --browsers Firefox +rm -rf dist/ || true +npm run build + +docker pull paketobuildpacks/builder-jammy-full:0.3.316 + +rm -rf app_server/ || true +mkdir app_server +mv dist/ app_server/dist + +pack -v \ +--path app_server \ +build \ +$REPO_IMAGE/$PROJECT_NAME:$VERSION \ +--env HTTP_PROXY="$HTTP_PROXY" \ +--env HTTPS_PROXY="$HTTPS_PROXY" \ +--env NO_PROXY="$NO_PROXY" \ +--env BP_NODE_OPTIMIZE_MEMORY=true \ +--env BP_HEALTH_CHECKER_ENABLED=true \ +--env BP_LAUNCHPOINT="dist/readonly-frontend/server/server.mjs" \ +--buildpack gcr.io/paketo-buildpacks/nodejs \ +--buildpack gcr.io/paketo-buildpacks/health-checker:latest \ +--builder paketobuildpacks/builder-jammy-full:0.3.316 + +rm -rf app_server + +# publish in docker hub +# docker login +# docker push $REPO_IMAGE/$PROJECT_NAME:$VERSION +# docker logout +# docker rmi $REPO_IMAGE/$PROJECT_NAME:$VERSION diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/karma.conf.js b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/karma.conf.js new file mode 100644 index 0000000..d592c0b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/karma.conf.js @@ -0,0 +1,40 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma'), + require("karma-firefox-launcher") + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, './coverage/readonly-frontend'), + subdir: '.', + reporters: [ + { type: 'html' }, + { type: 'text-summary' } + ] + }, + reporters: ['progress', 'kjhtml'], + browsers: ['Firefox'], + restartOnFileChange: true + }); +}; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/mock-data.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/mock-data.json new file mode 100644 index 0000000..e46311b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/mock-data.json @@ -0,0 +1,16 @@ +{ + "data":[ + { + "x": 1, + "y": 2.3 + }, + { + "x": 2, + "y": 5.6 + }, + { + "x": 3, + "y": 4.7 + } + ] + } \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/mock-server.js b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/mock-server.js new file mode 100644 index 0000000..d623435 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/mock-server.js @@ -0,0 +1,24 @@ +const jsonServer = require('json-server'); +const server = jsonServer.create(); +const router = jsonServer.router('mock-data.json'); +const middlewares = jsonServer.defaults(); + +server.use(middlewares); + +server.get('/logged-in', (req, res) => { + res.status(401).jsonp({ + redirect_to: "/somesome" + }) +}) + +server.get('/somesome', (req, res) => { + res.status(200).jsonp({ + "test": "aaaaaaaaaaaaaaaaaaaaaaaa!" + }) +}) + +server.use(router); + +server.listen(3000, () => { + console.log('JSON Server is running'); +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/package.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/package.json new file mode 100644 index 0000000..e6c23a0 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/package.json @@ -0,0 +1,51 @@ +{ + "name": "readonly-frontend", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test", + "dev:mock": "node mock-server.js", + "serve:ssr:readonly-frontend": "ng build --configuration development && node dist/readonly-frontend/server/server.mjs" + }, + "private": true, + "dependencies": { + "@angular/animations": "^17.0.0", + "@angular/cdk": "^17.0.1", + "@angular/common": "^17.0.0", + "@angular/compiler": "^17.0.0", + "@angular/core": "^17.0.0", + "@angular/forms": "^17.0.0", + "@angular/material": "^17.0.1", + "@angular/platform-browser": "^17.0.0", + "@angular/platform-browser-dynamic": "^17.0.0", + "@angular/platform-server": "^17.0.0", + "@angular/router": "^17.0.0", + "@angular/ssr": "^17.0.1", + "chart.js": "^4.4.0", + "express": "^4.18.2", + "ng2-charts": "^5.0.3", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.14.2" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^17.0.1", + "@angular/cli": "^17.0.1", + "@angular/compiler-cli": "^17.0.0", + "@types/express": "^4.17.17", + "@types/jasmine": "~5.1.0", + "@types/node": "^18.18.0", + "jasmine-core": "~5.1.0", + "json-server": "^0.17.4", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-firefox-launcher": "^2.1.2", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.2.2" + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/server.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/server.ts new file mode 100644 index 0000000..96014ed --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/server.ts @@ -0,0 +1,60 @@ +import { APP_BASE_HREF } from '@angular/common'; +import { CommonEngine } from '@angular/ssr'; +import express from 'express'; +import { fileURLToPath } from 'node:url'; +import { dirname, join, resolve } from 'node:path'; +import bootstrap from './src/main.server'; + +// The Express app is exported so that it can be used by serverless Functions. +export function app(): express.Express { + const server = express(); + const serverDistFolder = dirname(fileURLToPath(import.meta.url)); + const browserDistFolder = resolve(serverDistFolder, '../browser'); + const indexHtml = join(serverDistFolder, 'index.server.html'); + + const commonEngine = new CommonEngine(); + + server.set('view engine', 'html'); + server.set('views', browserDistFolder); + + server.get('/health', (req, res) => { + res.status(200).send('Ok'); + }); + + // Example Express Rest API endpoints + // server.get('/api/**', (req, res) => { }); + // Serve static files from /browser + server.get('*.*', express.static(browserDistFolder, { + maxAge: '1y' + })); + + // All regular routes use the Angular engine + server.get('*', (req, res, next) => { + const { protocol, originalUrl, baseUrl, headers } = req; + + commonEngine + .render({ + bootstrap, + documentFilePath: indexHtml, + url: `${protocol}://${headers.host}${originalUrl}`, + publicPath: browserDistFolder, + providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], + }) + .then((html) => res.send(html)) + .catch((err) => next(err)); + }); + + return server; +} + +function run(): void { + const port = process.env['PORT'] || 4000; + + // Start up the Node server + const server = app(); + server.listen(port, () => { + console.log(`Node Express server listening on http://localhost:${port}`); + }); +} + +run(); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.component.html new file mode 100644 index 0000000..a693b2b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.component.html @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.component.spec.ts new file mode 100644 index 0000000..899f072 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + let component: AppComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AppComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AppComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.component.ts new file mode 100644 index 0000000..ba49a19 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.component.ts @@ -0,0 +1,13 @@ +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + selector: 'app-root', + standalone: true, + imports: [CommonModule, RouterOutlet], + templateUrl: './app.component.html', + styleUrl: './app.component.scss' +}) +export class AppComponent { +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.config.server.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.config.server.ts new file mode 100644 index 0000000..b4d57c9 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.config.server.ts @@ -0,0 +1,11 @@ +import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; +import { provideServerRendering } from '@angular/platform-server'; +import { appConfig } from './app.config'; + +const serverConfig: ApplicationConfig = { + providers: [ + provideServerRendering() + ] +}; + +export const config = mergeApplicationConfig(appConfig, serverConfig); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.config.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.config.ts new file mode 100644 index 0000000..fc76adc --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.config.ts @@ -0,0 +1,15 @@ +import { ApplicationConfig, importProvidersFrom } from '@angular/core'; +import { provideRouter } from '@angular/router'; + +import { routes } from './app.routes'; +import { provideAnimations } from '@angular/platform-browser/animations'; +import { HttpClientModule } from '@angular/common/http'; +import { provideClientHydration } from '@angular/platform-browser'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideRouter(routes), + provideAnimations(), + importProvidersFrom(HttpClientModule), provideClientHydration() + ] +}; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.routes.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.routes.ts new file mode 100644 index 0000000..922940e --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.routes.ts @@ -0,0 +1,28 @@ +import { Routes } from '@angular/router'; +import { NotFoundComponent } from './not-found/not-found.component'; +import { ErrorComponent } from './error/error.component'; +import { LayoutComponent } from './layout/layout.component'; +import { LineChartComponent } from './line-chart/line-chart.component'; +import { authGuard } from './auth.guard'; + +export const routes: Routes = [ + { + path: 'error', + component: ErrorComponent + }, + { + path: '', + component: LayoutComponent, + canActivate: [authGuard], + children: [ + { + path: '', + component: LineChartComponent + } + ] + }, + { + path: '**', + component: NotFoundComponent + }, +]; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.service.spec.ts new file mode 100644 index 0000000..aa8a2ac --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.service.spec.ts @@ -0,0 +1,116 @@ +import { TestBed } from '@angular/core/testing'; + +import { AppService } from './app.service'; + +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { Point } from './models/point.model'; +import { environment } from '../environments/environment'; +import { ErrorService } from './error.service'; +import { HttpErrorResponse } from '@angular/common/http'; + +describe('AppService', () => { + let service: AppService; + let errorServiceSpy: jasmine.SpyObj; + let httpTestingController: HttpTestingController; + + let points: Point[] = [ + { + x: 1, + y: 1 + }, + { + x: 2, + y: 2 + }, + { + x: 3, + y: 3 + } + ]; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ HttpClientTestingModule ], + providers: [ + { + provide: ErrorService, useValue: jasmine.createSpyObj('ErrorService', ['handle']) + } + ] + }); + service = TestBed.inject(AppService); + errorServiceSpy = TestBed.inject(ErrorService) as jasmine.SpyObj; + httpTestingController = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpTestingController.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('getData', (done: DoneFn) => { + // given (instead of when) + + const dataHttpURL = `${environment.apiUrl}/data`; + + // when + + service.getData() + .subscribe({ + next: ps => { + expect(ps).toEqual(points); + done(); + }, + error: er => done.fail('data is expected') + }); + + const req = httpTestingController.expectOne(dataHttpURL); + req.flush(points); + + // then (instead of verify) + + expect(req.request.method).toEqual('GET'); + }); + + it('getData (fail)', done => { + // given (instead of when) + + const dataHttpURL = `${environment.apiUrl}/data`; + + const expectedErrorResponse = new HttpErrorResponse({ + error: '["password is invalid"]', + url: dataHttpURL, + status: 400, + statusText: 'Bad request' + }); + + // when + + service.getData() + .subscribe({ + next: ps => done.fail('change not expected'), + error: (actualErrorResponse: HttpErrorResponse) => { + expect(actualErrorResponse).toEqual(expectedErrorResponse); + done(); + } + }); + + + const req = httpTestingController.expectOne(dataHttpURL); + + req.flush(expectedErrorResponse.error, expectedErrorResponse); + + // then (instead of verify) + + expect(req.request.method).toEqual('GET'); + + expect(errorServiceSpy.handle.calls.count()) + .toBe(1); + expect(errorServiceSpy.handle.calls.first().args) + .toEqual([ expectedErrorResponse.error ]); + }); + +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.service.ts new file mode 100644 index 0000000..bdcc095 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/app.service.ts @@ -0,0 +1,26 @@ +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable, catchError, throwError } from 'rxjs'; +import { Point } from './models/point.model'; +import { environment } from '../environments/environment'; +import { ErrorService } from './error.service'; + +@Injectable({ + providedIn: 'root' +}) +export class AppService { + + constructor(private http: HttpClient, private errorService: ErrorService) {} + + getData(): Observable { + return this.http.get(`${environment.apiUrl}/data`) + .pipe( + catchError(this.errorHandler.bind(this)) + ); + } + + private errorHandler(error: HttpErrorResponse) { + this.errorService.handle(error.error) + return throwError(() => error) + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/auth.guard.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/auth.guard.spec.ts new file mode 100644 index 0000000..4cfca2c --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/auth.guard.spec.ts @@ -0,0 +1,163 @@ +import { TestBed } from '@angular/core/testing'; +import { ActivatedRouteSnapshot, CanActivateFn, RouterStateSnapshot } from '@angular/router'; + +import { authGuard } from './auth.guard'; +import { AuthService } from './auth.service'; +import { SaveFutureRedirectService } from './save-future-redirect.service'; +import { Observable, of, throwError } from 'rxjs'; +import { WindowService } from './window.service'; +import { environment } from '../environments/environment'; + +describe('authGuard', () => { + const executeGuard: CanActivateFn = (...guardParameters) => + TestBed.runInInjectionContext(() => authGuard(...guardParameters)); + + let authService: jasmine.SpyObj; + let saveFutureRedirectService: jasmine.SpyObj; + let windowService: jasmine.SpyObj; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers:[ + { + provide: AuthService, useValue: jasmine.createSpyObj('AuthService', ['checkAuthenticate']) + }, + { + provide: SaveFutureRedirectService, useValue: jasmine.createSpyObj('SaveFutureRedirectService', ['redirectAndClearIfExist','saveIfNotExist']) + }, + { + provide: WindowService, useValue: jasmine.createSpyObj('WindowService', ['get']) + } + ] + }); + + + + authService = TestBed.inject(AuthService) as jasmine.SpyObj; + saveFutureRedirectService = TestBed.inject(SaveFutureRedirectService) as jasmine.SpyObj; + windowService = TestBed.inject(WindowService) as jasmine.SpyObj; + }); + + it('should be created', () => { + expect(executeGuard).toBeTruthy(); + }); + + it('should be true', done => { + // given (instead of when) + + authService.checkAuthenticate.and.returnValue(of(void 0)); + + // when + + (executeGuard({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) as Observable) + .subscribe({ + next: isSuccess => { + expect(isSuccess).toEqual(true); + done(); + } + }); + + // then (instead of verify) + + expect(authService.checkAuthenticate.calls.count()) + .toBe(1); + + expect(saveFutureRedirectService.redirectAndClearIfExist.calls.count()) + .toBe(1); + + + }); + + it('should be false', done => { + // given (instead of when) + + const error = { + "error":{ + "redirect_to": "/someroute" + }, + status: 401 + }; + + + const window = { + "location": { + "href": {} + } + } + + authService.checkAuthenticate.and.returnValue(new Observable(s => { + s.error(error); + s.complete(); + })); + + windowService.get.and.returnValue(window); + + // when + + (executeGuard({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) as Observable) + .subscribe({ + next: isSuccess => { + expect(window.location.href).toEqual(`${environment.apiUrl}${error.error.redirect_to}`); + expect(isSuccess).toEqual(false); + done(); + }, + error: error => done.fail('never call') + }); + + // then (instead of verify) + + expect(authService.checkAuthenticate.calls.count()) + .toBe(1); + + expect(saveFutureRedirectService.saveIfNotExist.calls.count()) + .toBe(1); + + + }); + + + it('should be false 2', done => { + // given (instead of when) + + const error = { + "error":{ + "redirect_to": "/someroute" + }, + status: 404 + }; + + + const window = { + "location": { + "href": {} + } + } + + authService.checkAuthenticate.and.returnValue(new Observable(s => { + s.error(error); + s.complete(); + })); + + windowService.get.and.returnValue(window); + + // when + + (executeGuard({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) as Observable) + .subscribe({ + next: isSuccess => { + expect(window.location.href).toEqual(`/error`); + expect(isSuccess).toEqual(false); + done(); + }, + error: error => done.fail('never call') + }); + + // then (instead of verify) + + expect(authService.checkAuthenticate.calls.count()) + .toBe(1); + + + }); + +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/auth.guard.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/auth.guard.ts new file mode 100644 index 0000000..1268ed5 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/auth.guard.ts @@ -0,0 +1,42 @@ +import { CanActivateFn } from '@angular/router'; +import { AuthService } from './auth.service'; +import { inject } from '@angular/core'; +import { SaveFutureRedirectService } from './save-future-redirect.service'; +import { catchError, first, map, of, tap } from 'rxjs'; +import { environment } from '../environments/environment'; +import { WindowService } from './window.service'; + +export const authGuard: CanActivateFn = (route, state) => { + + const authService = inject(AuthService); + const saveFutureRedirectService = inject(SaveFutureRedirectService); + const windowService = inject(WindowService); + + return authService.checkAuthenticate() + .pipe( + first(), + + map(() => { + + //получаем сохраненый url, удаляем и перередиректуемся + saveFutureRedirectService.redirectAndClearIfExist(); + + return true; + }), + catchError(error => { + + if (error?.error?.redirect_to && error?.status === 401) { + + //запоминаем в cookie текущий url + saveFutureRedirectService.saveIfNotExist(); + + // перенаправляем напрямую приложение + windowService.get().location.href = `${environment.apiUrl}${error.error.redirect_to}`; + } else { + windowService.get().location.href = '/error' + } + + return of(false); + }) + ); +}; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/auth.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/auth.service.spec.ts new file mode 100644 index 0000000..705abc8 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/auth.service.spec.ts @@ -0,0 +1,113 @@ +import { TestBed } from '@angular/core/testing'; + +import { AuthService } from './auth.service'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { AppService } from './app.service'; +import { Observable, of } from 'rxjs'; +import { environment } from '../environments/environment'; +import { HttpErrorResponse } from '@angular/common/http'; + +describe('AuthService', () => { + let service: AuthService; + let httpTestingController: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ HttpClientTestingModule ], + providers: [ AppService ] + }); + service = TestBed.inject(AuthService); + httpTestingController = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpTestingController.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('checkAuthenticate should be succeed', done => { + // given (instead of when) + + // when + + service.checkAuthenticate() + .subscribe({ + next: () => { + done() + }, + error: done.fail + }); + + const req = httpTestingController.expectOne(`${environment.apiUrl}/logged-in`); + + req.flush(null); + + // then (instead of verify) + + expect(req.request.method).toEqual('GET'); + + }); + + it('checkAuthenticate should be fail', done => { + // given (instead of when) + + const registrationHttpURL = `${environment.apiUrl}/logged-in`; + + const expectedErrorResponse = new HttpErrorResponse({ + error: '{"redirect_to":"/testtest"}', + url: registrationHttpURL, + status: 401, + statusText: 'Bad request' + }); + + + // when + + service.checkAuthenticate() + .subscribe({ + next: () => done.fail('data not expected'), + error: (actualErrorResponse: HttpErrorResponse) => { + expect(actualErrorResponse).toEqual(expectedErrorResponse); + done(); + } + }); + + const req = httpTestingController.expectOne(registrationHttpURL); + + req.flush(expectedErrorResponse.error, expectedErrorResponse); + + // then (instead of verify) + + expect(req.request.method).toEqual('GET'); + + }); + + it('logout should be succeed', done => { + // given (instead of when) + + // when + + service.logout() + .subscribe({ + next: () => { + done() + }, + error: done.fail + }); + + const req = httpTestingController.expectOne(`${environment.apiUrl}/logout`); + + req.flush(null); + + // then (instead of verify) + + expect(req.request.method).toEqual('POST'); + + }); + + +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/auth.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/auth.service.ts new file mode 100644 index 0000000..a81e38f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/auth.service.ts @@ -0,0 +1,20 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { environment } from '../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + + constructor(private http: HttpClient) {} + + checkAuthenticate(): Observable { + return this.http.get(`${environment.apiUrl}/logged-in`); + } + + logout(): Observable{ + return this.http.post(`${environment.apiUrl}/logout`, {}); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/cookie.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/cookie.service.spec.ts new file mode 100644 index 0000000..5f96955 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/cookie.service.spec.ts @@ -0,0 +1,61 @@ +import { TestBed } from '@angular/core/testing'; + +import { CookieService } from './cookie.service'; + +describe('CookieService', () => { + let service: CookieService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + CookieService + ] + }); + service = TestBed.inject(CookieService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('setCookie, getCookie and deleteCookie', () => { + // given (instead of when) + + const expectedNotExistCookieValue: string | undefined = undefined; + + const expectedSomeCookie = { + name: 'myCookieName', + value: 'myCookieValue', + options: { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax', + expires: new Date((new Date()).getTime() + 1) + } + }; + + const expectedSomeCookie2 = { + name: 'myCookieName2', + value: 'myCookieValue2' + }; + + // when + + const actualNotExistCookieValue = service.getCookie('notExistCookie'); + + service.setCookie(expectedSomeCookie.name, expectedSomeCookie.value, expectedSomeCookie.options); + const actualSomeCookieValue = service.getCookie(expectedSomeCookie.name); + + service.setCookie(expectedSomeCookie2.name, expectedSomeCookie2.value); + service.deleteCookie(expectedSomeCookie2.name); + const actualSomeCookieValue2 = service.getCookie(expectedSomeCookie2.name); + + // then (instead of verify) + + expect(actualNotExistCookieValue).toEqual(expectedNotExistCookieValue); + expect(actualSomeCookieValue).toEqual(expectedSomeCookie.value); + expect(actualSomeCookieValue2).toEqual(expectedNotExistCookieValue); + + }) +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/cookie.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/cookie.service.ts new file mode 100644 index 0000000..2c7f197 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/cookie.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class CookieService { + + // возвращает куки с указанным name, + // или undefined, если ничего не найдено + getCookie(name: string): string | undefined { + + let matches = document.cookie.match(new RegExp( + "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)" + )); + return matches ? decodeURIComponent(matches[1]) : undefined; + } + + setCookie(name: string, value: string, options: any = {} ) { + + options = { + path: '/', + // при необходимости добавьте другие значения по умолчанию + ...options + }; + + if (options.expires instanceof Date) { + options.expires = options.expires.toUTCString(); + } + + let updatedCookie = encodeURIComponent(name) + "=" + encodeURIComponent(value); + + for (let optionKey in options) { + updatedCookie += "; " + optionKey; + let optionValue = options[optionKey]; + if (optionValue !== true) { + updatedCookie += "=" + optionValue; + } + } + + document.cookie = updatedCookie; + } + + deleteCookie(name: string) { + this.setCookie(name, "", { + 'max-age': -1 + }) + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error.service.spec.ts new file mode 100644 index 0000000..1716c7a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error.service.spec.ts @@ -0,0 +1,50 @@ +import { TestBed } from '@angular/core/testing'; + +import { ErrorService } from './error.service'; +import { MatSnackBar, MatSnackBarConfig} from '@angular/material/snack-bar'; + +describe('ErrorService', () => { + let service: ErrorService; + let matSnackBarSpy: jasmine.SpyObj; + + beforeEach(() => { + + TestBed.configureTestingModule({ + providers: [ + ErrorService, + { + provide: MatSnackBar, useValue: jasmine.createSpyObj('MatSnackBar', ['open']) + } + ] + }); + service = TestBed.inject(ErrorService); + matSnackBarSpy = TestBed.inject(MatSnackBar) as jasmine.SpyObj; + + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('handle', () => { + // given (instead of when) + + const expectedArgsOpen: [message: string, action?: string | undefined, config?: MatSnackBarConfig | undefined] = [ + 'Some Error', + service.actionCloseButtonName, + service.confOptionsSnackbar + ]; + + // when + + service.handle(expectedArgsOpen[0]); + + // then (instead of verify) + + expect(matSnackBarSpy.open.calls.count()) + .toBe(1); + expect(matSnackBarSpy.open.calls.first().args) + .toEqual(expectedArgsOpen); + }); + +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error.service.ts new file mode 100644 index 0000000..86d46c0 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error.service.ts @@ -0,0 +1,21 @@ +import {Injectable} from '@angular/core' +import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar' + +@Injectable({ + providedIn: 'root' +}) +export class ErrorService { + + readonly actionCloseButtonName = 'CLOSE'; + + readonly confOptionsSnackbar: MatSnackBarConfig = { + duration: 5000 + }; + + constructor(private matSnackBar: MatSnackBar){} + + handle(message: string) { + this.matSnackBar.open(message, this.actionCloseButtonName, this.confOptionsSnackbar); + } + +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error/error.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error/error.component.html new file mode 100644 index 0000000..d0528a6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error/error.component.html @@ -0,0 +1,16 @@ + + + logo +

Пришла ошибка от сервера аутентификации и авторизации

+ +

{{ errorOnPageModel.error }}

+ +

{{ errorOnPageModel.errorDescription }}

+ + + + cat_401 +
\ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error/error.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error/error.component.scss new file mode 100644 index 0000000..3b7d5c0 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error/error.component.scss @@ -0,0 +1,9 @@ +:host { + min-width: inherit; + width: inherit; + min-height: inherit; + height: inherit; + display: flex; + flex-direction: column; + align-items: center; +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error/error.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error/error.component.spec.ts new file mode 100644 index 0000000..b647724 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error/error.component.spec.ts @@ -0,0 +1,161 @@ +import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing'; + +import { RouterTestingHarness } from '@angular/router/testing'; + +import { ErrorComponent } from './error.component'; +import { ErrorOnPageModel } from '../models/error-on-page.model'; +import { ErrorResponseCode } from '../models/error-response-code.model'; +import { By } from '@angular/platform-browser'; +import { provideRouter } from '@angular/router'; +import { WindowService } from '../window.service'; + +describe('ErrorComponent', () => { + + let harness: RouterTestingHarness; + + let error: string = '111'; + let error_description: string = '222'; + let reauthentication_location: string = '/qqq'; + + let windowServiceSpy: jasmine.SpyObj; + + beforeEach(async () => { + + await TestBed.configureTestingModule({ + imports: [ErrorComponent], + providers: [ + provideRouter([ + { + path: 'error', + component: ErrorComponent + } + ]), + { + provide: WindowService, useValue: jasmine.createSpyObj('WindowService', ['get']) + }, + ] + }) + .compileComponents() + .then(async () => { + harness = await RouterTestingHarness.create(); + + windowServiceSpy = TestBed.inject(WindowService) as jasmine.SpyObj; + + }); + }); + + it('should create', done => { + harness.navigateByUrl(`/error`, ErrorComponent) + .then(comp => { + expect(comp).toBeTruthy(); + + comp + .errorOnPageModel$ + .subscribe( + { + next: el => { + + expect(`Неизвестная ошибка`).toEqual(el.error); + expect("Неизвестное описание").toEqual(el.errorDescription); + expect('/').toEqual(el.reauthenticationLocation); + + done(); + }, + error: er => done.fail("errorOnPageModel is incorrect") + }); + }); + + }); + + it('should create with query param', done => { + + harness.navigateByUrl(`/error?error=${error}&error_description=${error_description}&reauthentication_location=${reauthentication_location}`, ErrorComponent) + .then(compWithQueryParam => { + //when + expect(compWithQueryParam).toBeTruthy(); + + compWithQueryParam + .errorOnPageModel$ + .subscribe( + { + next: el => { + + expect(`Ошибка (${error})`).toEqual(el.error); + expect(error_description).toEqual(el.errorDescription); + expect(reauthentication_location).toEqual(el.reauthenticationLocation); + + done(); + }, + error: er => done.fail("errorOnPageModel is incorrect") + } + ); + + }); + + }); + + it('getErrorResponseCodeRus', async () => { + + // given (instead of when) + await harness.navigateByUrl(`/error`, ErrorComponent); + harness.detectChanges(); + + let h2 = harness.routeDebugElement!.query(By.css("h2")).nativeElement as HTMLParagraphElement; + + // when + expect(h2.textContent).toEqual("Неизвестная ошибка"); + + + let changeAndCheckError = async (currentErrorResponseCode: ErrorResponseCode) => { + await harness.navigateByUrl(`/`); + await harness.navigateByUrl(`/error?error=${currentErrorResponseCode}`, ErrorComponent); + harness.detectChanges(); + h2 = harness.routeDebugElement!.query(By.css("h2")).nativeElement as HTMLParagraphElement; + expect(h2.textContent).toEqual(ErrorOnPageModel.getErrorResponseCodeRus(currentErrorResponseCode)); + } + + await changeAndCheckError(ErrorResponseCode.AccessDenied); + await changeAndCheckError(ErrorResponseCode.InsufficientScope); + await changeAndCheckError(ErrorResponseCode.InvalidClient); + await changeAndCheckError(ErrorResponseCode.InvalidGrant); + await changeAndCheckError(ErrorResponseCode.InvalidRedirectUri); + await changeAndCheckError(ErrorResponseCode.InvalidRequest); + await changeAndCheckError(ErrorResponseCode.InvalidScope); + await changeAndCheckError(ErrorResponseCode.InvalidToken); + await changeAndCheckError(ErrorResponseCode.ServerError); + await changeAndCheckError(ErrorResponseCode.TemporarilyUnavailable); + await changeAndCheckError(ErrorResponseCode.UnauthorizedClient); + await changeAndCheckError(ErrorResponseCode.UnsupportedGrantType); + await changeAndCheckError(ErrorResponseCode.UnsupportedResponseType); + await changeAndCheckError(ErrorResponseCode.UnsupportedTokenType); + + }); + + + it('goToAuth', fakeAsync(async () => { + + // given (instead of when) + + const window = { + "location": { + "href": {} + } + } + + const comp = await harness.navigateByUrl(`/error?error=${error}&error_description=${error_description}&reauthentication_location=${reauthentication_location}`, ErrorComponent); + harness.detectChanges(); + + windowServiceSpy.get.and.returnValue(window); + + // when + expect(comp).toBeTruthy(); + + harness.detectChanges(); + let button = harness.routeDebugElement!.query(By.css("button[mat-raised-button]")).nativeElement as HTMLButtonElement; + button.click(); + harness.detectChanges(); + + expect(window.location.href).toEqual(reauthentication_location); + })); + +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error/error.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error/error.component.ts new file mode 100644 index 0000000..f4ff830 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/error/error.component.ts @@ -0,0 +1,68 @@ +import { Component, OnDestroy } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Observable, Subject, first, map, takeUntil } from 'rxjs'; +import { ErrorOnPageModel } from '../models/error-on-page.model'; +import { ActivatedRoute } from '@angular/router'; + +import {MatButtonModule} from '@angular/material/button'; +import { WindowService } from '../window.service'; + +@Component({ + selector: 'app-error', + standalone: true, + imports: [CommonModule, MatButtonModule], + templateUrl: './error.component.html', + styleUrl: './error.component.scss' +}) +export class ErrorComponent implements OnDestroy { + // для очистки памяти rxjs + private readonly onDestroy = new Subject(); + + errorOnPageModel$: Observable; + + constructor( + private windowService: WindowService, + private route: ActivatedRoute + ) { + + this.errorOnPageModel$ = this.route.queryParamMap + .pipe( + first(), + map( + (paramMap) => { + let error: string | null = paramMap.get("error"); + let errorDescription: string | null = paramMap.get("error_description"); + let reauthenticationLocation: string | null = paramMap.get("reauthentication_location"); + + if (error) { + error = ErrorOnPageModel.getErrorResponseCodeRus(error); + } else { + error = "Неизвестная ошибка" + } + + if (!errorDescription) { + errorDescription = "Неизвестное описание" + } + + if (!reauthenticationLocation) { + reauthenticationLocation = '/'; + } + + return new ErrorOnPageModel(error, errorDescription, reauthenticationLocation); + } + ), + takeUntil(this.onDestroy) + ) + } + + goToAuth(url: string) { + this.windowService.get().location.href = url; + } + + ngOnDestroy(): void { + this.onDestroy.next(); + this.onDestroy.complete(); + } + + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/generation-cookie-csrf.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/generation-cookie-csrf.service.spec.ts new file mode 100644 index 0000000..59cb84f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/generation-cookie-csrf.service.spec.ts @@ -0,0 +1,51 @@ +import { TestBed } from '@angular/core/testing'; + +import { GenerationCookieCsrfService } from './generation-cookie-csrf.service'; +import { CookieService } from './cookie.service'; + +describe('GenerationCookieCsrfService', () => { + let service: GenerationCookieCsrfService; + let cookieServiceSpy: jasmine.SpyObj; + + beforeEach(() => { + + TestBed.configureTestingModule({ + providers: [ + GenerationCookieCsrfService, + { + provide: CookieService, useValue: jasmine.createSpyObj('CookieService', ['setCookie']) + } + ] + }); + service = TestBed.inject(GenerationCookieCsrfService); + cookieServiceSpy = TestBed.inject(CookieService) as jasmine.SpyObj; + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('generateCookieCsrf', () => { + // given (instead of when) + + const expectedCookieName = service.xsrfTokenCookieName; + + let actualCookieName: string = ''; + let actualCookieValue: string = ''; + cookieServiceSpy.setCookie.and.callFake( (name, value, options) => { + actualCookieName = name; + actualCookieValue = value; + }) + + // when + + service.generateCookieCsrf(); + + // then (instead of verify) + + expect(cookieServiceSpy.setCookie.calls.count()) + .toBe(1); + expect(actualCookieName).toEqual(expectedCookieName); + expect(actualCookieValue).toBeTruthy(); + }); +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/generation-cookie-csrf.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/generation-cookie-csrf.service.ts new file mode 100644 index 0000000..bcffb12 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/generation-cookie-csrf.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@angular/core'; +import { CookieService } from './cookie.service'; + +@Injectable({ + providedIn: 'root' +}) +export class GenerationCookieCsrfService { + + + readonly xsrfTokenCookieName = "XSRF-TOKEN"; + + constructor( + private cookieService: CookieService + ) { } + + generateCookieCsrf(){ + + //устанавливаем cookie от CSRF + this.cookieService.setCookie(this.xsrfTokenCookieName, this.generateRandomString(20), { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax' + }); + + } + + private generateRandomString(length: number): string { + + let result: string = ''; + let characters: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let charactersLength: number = characters.length; + + for (let i: number = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + + return result; + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/layout/layout.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/layout/layout.component.html new file mode 100644 index 0000000..921efdd --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/layout/layout.component.html @@ -0,0 +1,8 @@ + + APP READONLY + + + + \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/layout/layout.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/layout/layout.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/layout/layout.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/layout/layout.component.spec.ts new file mode 100644 index 0000000..7eae0c5 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/layout/layout.component.spec.ts @@ -0,0 +1,80 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LayoutComponent } from './layout.component'; +import { GenerationCookieCsrfService } from '../generation-cookie-csrf.service'; +import { AuthService } from '../auth.service'; +import { SaveFutureRedirectService } from '../save-future-redirect.service'; +import { WindowService } from '../window.service'; +import { of } from 'rxjs'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; + +describe('LayoutComponent', () => { + let component: LayoutComponent; + let fixture: ComponentFixture; + + let generationCookieCsrfServiceSpy: jasmine.SpyObj; + let authServiceSpy: jasmine.SpyObj; + let saveFutureRedirectServiceSpy: jasmine.SpyObj; + let windowServiceSpy: jasmine.SpyObj; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LayoutComponent, HttpClientTestingModule], + providers: [ + { + provide: GenerationCookieCsrfService, useValue: jasmine.createSpyObj('GenerationCookieCsrfService', ['generateCookieCsrf']) + }, + { + provide: AuthService, useValue: jasmine.createSpyObj('AuthService', ['logout']) + }, + { + provide: SaveFutureRedirectService, useValue: jasmine.createSpyObj('SaveFutureRedirectService', ['saveIfNotExist']) + }, + { + provide: WindowService, useValue: jasmine.createSpyObj('WindowService', ['get']) + } + ] + }) + .compileComponents(); + + generationCookieCsrfServiceSpy = TestBed.inject(GenerationCookieCsrfService) as jasmine.SpyObj; + authServiceSpy = TestBed.inject(AuthService) as jasmine.SpyObj; + saveFutureRedirectServiceSpy = TestBed.inject(SaveFutureRedirectService) as jasmine.SpyObj; + windowServiceSpy = TestBed.inject(WindowService) as jasmine.SpyObj; + + fixture = TestBed.createComponent(LayoutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('logout', () => { + + const window = { + "location": { + "href": {} + } + } + + generationCookieCsrfServiceSpy.generateCookieCsrf.and.returnValue(); + + saveFutureRedirectServiceSpy.saveIfNotExist.and.returnValue(); + + windowServiceSpy.get.and.returnValue(window); + + authServiceSpy.logout.and.returnValue(of(void 0)); + + component.logout(); + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext('generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() == 1') + .toBe(1); + expect(saveFutureRedirectServiceSpy.saveIfNotExist.calls.count()) + .withContext('saveFutureRedirectServiceSpy.saveIfNotExist.calls.count() == 2') + .toBe(2); + + }); +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/layout/layout.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/layout/layout.component.ts new file mode 100644 index 0000000..e911e13 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/layout/layout.component.ts @@ -0,0 +1,64 @@ +import { Component, OnDestroy } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Subject, takeUntil } from 'rxjs'; +import { environment } from '../../environments/environment'; +import { GenerationCookieCsrfService } from '../generation-cookie-csrf.service'; +import { AuthService } from '../auth.service'; +import { SaveFutureRedirectService } from '../save-future-redirect.service'; +import { RouterOutlet } from '@angular/router'; + +import {MatToolbarModule} from '@angular/material/toolbar'; +import {MatButtonModule} from '@angular/material/button'; +import { WindowService } from '../window.service'; + + +@Component({ + selector: 'app-layout', + standalone: true, + imports: [CommonModule, RouterOutlet, MatToolbarModule, MatButtonModule], + templateUrl: './layout.component.html', + styleUrl: './layout.component.scss' +}) +export class LayoutComponent implements OnDestroy { + + // для очистки памяти rxjs + private readonly onDestroy = new Subject(); + + constructor(private generationCookieCsrfService: GenerationCookieCsrfService, + private authService: AuthService, + private saveFutureRedirectService: SaveFutureRedirectService, + private windowService: WindowService + ) { + + } + + logout() { + + this.generationCookieCsrfService.generateCookieCsrf(); + + let logoutHandler: (() => void) = () => { + + //запоминаем в cookie текущий url + this.saveFutureRedirectService.saveIfNotExist(); + + this.windowService.get().location.href = `${environment.authServerBaseUrl}/oauth2/sessions/logout`; + }; + + this.authService.logout() + .pipe( + takeUntil(this.onDestroy) + ) + .subscribe({ + next: logoutHandler, + error: logoutHandler, + complete: logoutHandler + }); + + + } + + ngOnDestroy(): void { + this.onDestroy.next(); + this.onDestroy.complete(); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/line-chart/line-chart.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/line-chart/line-chart.component.html new file mode 100644 index 0000000..9bc83d7 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/line-chart/line-chart.component.html @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/line-chart/line-chart.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/line-chart/line-chart.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/line-chart/line-chart.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/line-chart/line-chart.component.spec.ts new file mode 100644 index 0000000..994244b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/line-chart/line-chart.component.spec.ts @@ -0,0 +1,75 @@ +import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; + +import { LineChartComponent } from './line-chart.component'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { AppService } from '../app.service'; +import { Point } from '../models/point.model'; +import { last, lastValueFrom, of } from 'rxjs'; + +describe('LineChartComponent', () => { + let component: LineChartComponent; + let fixture: ComponentFixture; + let appServiceSpy: jasmine.SpyObj; + + let points: Point[] = [ + { + x: 1, + y: 1 + }, + { + x: 2, + y: 2 + }, + { + x: 3, + y: 3 + } + ]; + + let x: number[] = points.map((value) => value.x); + let y: number[] = points.map((value) => value.y); + + beforeEach(async () => { + + await TestBed.configureTestingModule({ + imports: [LineChartComponent] , + providers: [ + { + provide: AppService, useValue: jasmine.createSpyObj('AppService', ['getData']) + } + ] + }) + .compileComponents(); + + }); + + + beforeEach(() => { + + appServiceSpy = TestBed.inject(AppService) as jasmine.SpyObj; + + appServiceSpy.getData.and.returnValue(of(points)); + + fixture = TestBed.createComponent(LineChartComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + + }); + + it('should create', (done: DoneFn) => { + expect(component).toBeTruthy(); + + component.lineChartData$ + .subscribe({ + next: lineChartData => { + expect(lineChartData.datasets.length).toEqual(1); + expect(lineChartData.datasets[0].data).toEqual(x); + expect(lineChartData.labels).toEqual(y); + done(); + }, + error: er => done.fail('not error') + }); + }); + + +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/line-chart/line-chart.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/line-chart/line-chart.component.ts new file mode 100644 index 0000000..abba83f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/line-chart/line-chart.component.ts @@ -0,0 +1,82 @@ +import { Component, Inject, PLATFORM_ID, OnDestroy } from '@angular/core'; +import { CommonModule, isPlatformBrowser } from '@angular/common'; +import { Observable, Subject, first, map, takeUntil } from 'rxjs'; +import { ChartConfiguration, ChartType } from 'chart.js'; +import { AppService } from '../app.service'; +import { NgChartsModule } from 'ng2-charts'; + +@Component({ + selector: 'app-line-chart', + standalone: true, + imports: [CommonModule, NgChartsModule], + templateUrl: './line-chart.component.html', + styleUrl: './line-chart.component.scss' +}) +export class LineChartComponent implements OnDestroy { + + // для очистки памяти rxjs + private readonly onDestroy = new Subject(); + + public isBrowser: boolean = false; + + public lineChartOptions: ChartConfiguration['options'] = { + elements: { + line: { + tension: 0.5, + }, + }, + scales: { + y: { + position: 'left', + } + }, + plugins: { + legend: { display: true }, + }, + }; + public lineChartType: ChartType = 'line'; + + public lineChartData$: Observable + + constructor(@Inject(PLATFORM_ID) private platformId: any, + private appService: AppService + ) { + this.lineChartData$ = this.appService.getData() + .pipe( + first(), + map(points => { + let x: number[] = points.map((value) => value.x); + let y: number[] = points.map((value) => value.y); + + return { + datasets: [ + { + data: y, + label: 'some data', + backgroundColor: 'rgba(255,0,0,0.3)', + borderColor: 'red', + pointBackgroundColor: 'rgba(148,159,177,1)', + pointBorderColor: '#fff', + pointHoverBackgroundColor: '#fff', + pointHoverBorderColor: 'rgba(148,159,177,0.8)', + fill: 'origin', + }, + ], + labels: x, + } + + }), + takeUntil(this.onDestroy) + ) + + } + + ngOnInit(): void { + this.isBrowser = isPlatformBrowser(this.platformId); + } + + ngOnDestroy(): void { + this.onDestroy.next(); + this.onDestroy.complete(); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/models/error-on-page.model.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/models/error-on-page.model.ts new file mode 100644 index 0000000..9e80220 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/models/error-on-page.model.ts @@ -0,0 +1,51 @@ +import { ErrorResponseCode } from "./error-response-code.model"; + +export class ErrorOnPageModel{ + error: string; + errorDescription: string; + reauthenticationLocation: string; + + constructor(error: string, errorDescription: string, reauthenticationLocation: string){ + this.error = error; + this.errorDescription = errorDescription; + this.reauthenticationLocation = reauthenticationLocation; + } + + + public static getErrorResponseCodeRus(errorResponseCodeStr: string): string { + let errorResponseCode: ErrorResponseCode = errorResponseCodeStr as ErrorResponseCode; + switch (errorResponseCode) { + case ErrorResponseCode.InvalidRequest: + return "Ошибка параметров запроса (invalid_request)"; + case ErrorResponseCode.UnauthorizedClient: + return "Клиент не может быть авторизован используя данный метод (unauthorized_client)"; + case ErrorResponseCode.AccessDenied: + return "Владелец ресурса или сервер авторизации отклонили запрос (access_denied)"; + case ErrorResponseCode.UnsupportedResponseType: + return "Сервер авторизации не поддерживает получение кода авторизации с помощью этого метода (unsupported_response_type)"; + case ErrorResponseCode.InvalidScope: + return "Запрошенные scopes недопустимы, неизвестны или имеют неправильную форму (invalid_scope)"; + case ErrorResponseCode.ServerError: + return "Сервер авторизации столкнулся с неожиданным условием, которое помешало ему выполнить запрос (server_error)"; + case ErrorResponseCode.TemporarilyUnavailable: + return "Сервер авторизации в настоящее время не может обработать запрос из-за временной перегрузки или технического обслуживания сервера (temporarily_unavailable)"; + case ErrorResponseCode.InsufficientScope: + return "Запрос требует более высоких привилегий, чем те, которые предоставляются токеном доступа (insufficient_scope)"; + case ErrorResponseCode.InvalidClient: + return "Ошибка проверки подлинности клиента (например, неизвестный клиент, проверка подлинности клиента не включена или неподдерживаемый метод проверки подлинности) (invalid_client)"; + case ErrorResponseCode.InvalidGrant: + return "Предоставленное разрешение на авторизацию (например, код авторизации, учетные данные владельца ресурса) или токен обновления недействительны, истек срок действия, отозваны, не соответствуют URI перенаправления, использованному в запросе на авторизацию, или были выданы другому клиенту. (invalid_grant)"; + case ErrorResponseCode.InvalidRedirectUri: + return "Значение одного или нескольких URI перенаправления недопустимо (invalid_redirect_uri)"; + case ErrorResponseCode.InvalidToken: + return "Предоставленный маркер доступа истек, отозван, имеет неправильную форму или недействителен по другим причинам (invalid_token)"; + case ErrorResponseCode.UnsupportedGrantType: + return "Тип предоставления авторизации не поддерживается сервером авторизации (unsupported_grant_type)"; + case ErrorResponseCode.UnsupportedTokenType: + return "Сервер авторизации не поддерживает отзыв представленного типа токена (unsupported_token_type)"; + default: + return `Ошибка (${errorResponseCodeStr})`; + } + } + +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/models/error-response-code.model.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/models/error-response-code.model.ts new file mode 100644 index 0000000..182aec1 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/models/error-response-code.model.ts @@ -0,0 +1,61 @@ +export const enum ErrorResponseCode { + + // invalid_request + // The request is missing a required parameter, includes an + // invalid parameter value, includes a parameter more than + // once, or is otherwise malformed. + InvalidRequest = "invalid_request", + + // unauthorized_client + // The client is not authorized to request an authorization + // code using this method. + UnauthorizedClient = "unauthorized_client", + + // access_denied + // The resource owner or authorization server denied the + // request. + AccessDenied = "access_denied", + + // unsupported_response_type + // The authorization server does not support obtaining an + // authorization code using this method. + UnsupportedResponseType = "unsupported_response_type", + + // invalid_scope + // The requested scope is invalid, unknown, or malformed. + InvalidScope = "invalid_scope", + + // server_error + // The authorization server encountered an unexpected + // condition that prevented it from fulfilling the request. + // (This error code is needed because a 500 Internal Server + // Error HTTP status code cannot be returned to the client + // via an HTTP redirect.) + ServerError = "server_error", + + // temporarily_unavailable + // The authorization server is currently unable to handle + // the request due to a temporary overloading or maintenance + // of the server. (This error code is needed because a 503 + // Service Unavailable HTTP status code cannot be returned + // to the client via an HTTP redirect.) + TemporarilyUnavailable = "temporarily_unavailable", + // insufficient_scope - + // The request requires higher privileges than provided by the access token. + InsufficientScope = "insufficient_scope", + // invalid_client - Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method) + InvalidClient = "invalid_client", + // invalid_grant - The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, + // revoked, does not match the redirection URI used in the authorization request, or was issued to another client. + InvalidGrant = "invalid_grant", + // invalid_redirect_uri - The value of one or more redirection URIs is invalid. + InvalidRedirectUri = "invalid_redirect_uri", + // invalid_token - The access token provided is expired, revoked, malformed, or invalid for other reasons + InvalidToken = "invalid_token", + //unsupported_grant_type - The authorization grant type is not supported by the authorization server. + UnsupportedGrantType = "unsupported_grant_type", + // unsupported_token_type - The authorization server does not support the revocation of the presented token type + UnsupportedTokenType = "unsupported_token_type" + + + } \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/models/point.model.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/models/point.model.ts new file mode 100644 index 0000000..adafca1 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/models/point.model.ts @@ -0,0 +1,9 @@ +export class Point{ + x: number; + y: number; + + constructor(x: number, y: number){ + this.x = x; + this.y = y; + } + } \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/not-found/not-found.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/not-found/not-found.component.html new file mode 100644 index 0000000..2a7105c --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/not-found/not-found.component.html @@ -0,0 +1,11 @@ +
+

404

+
Page not found
+ cry +
diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/not-found/not-found.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/not-found/not-found.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/not-found/not-found.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/not-found/not-found.component.spec.ts new file mode 100644 index 0000000..7469fb3 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/not-found/not-found.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NotFoundComponent } from './not-found.component'; + +describe('NotFoundComponent', () => { + let component: NotFoundComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [NotFoundComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(NotFoundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/not-found/not-found.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/not-found/not-found.component.ts new file mode 100644 index 0000000..a4b7270 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/not-found/not-found.component.ts @@ -0,0 +1,13 @@ +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@Component({ + selector: 'app-not-found', + standalone: true, + imports: [CommonModule], + templateUrl: './not-found.component.html', + styleUrl: './not-found.component.scss' +}) +export class NotFoundComponent { + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/save-future-redirect.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/save-future-redirect.service.spec.ts new file mode 100644 index 0000000..05bbbed --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/save-future-redirect.service.spec.ts @@ -0,0 +1,87 @@ +import { TestBed } from '@angular/core/testing'; + +import { SaveFutureRedirectService } from './save-future-redirect.service'; +import { CookieService } from './cookie.service'; +import { Router, provideRouter } from '@angular/router'; +import { routes } from './app.routes'; + +describe('SaveFutureRedirectService', () => { + let service: SaveFutureRedirectService; + let cookieServiceSpy: jasmine.SpyObj; + let router: jasmine.SpyObj; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + { + provide: CookieService, useValue: jasmine.createSpyObj('CookieService', ['setCookie', 'getCookie', 'deleteCookie']) + }, + { + provide: Router, useValue: jasmine.createSpyObj('Router', ['navigateByUrl']) + }, + ] + }); + service = TestBed.inject(SaveFutureRedirectService); + cookieServiceSpy = TestBed.inject(CookieService) as jasmine.SpyObj; + + router = TestBed.inject(Router) as jasmine.SpyObj; + + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('saveIfNotExist', () => { + // given (instead of when) + + const expectedCookieName = service.urlCookieName; + + let actualCookieName: string = 'n'; + let actualCookieValue: string = 'v'; + + cookieServiceSpy.getCookie.and.returnValue(undefined); + + cookieServiceSpy.setCookie.and.callFake( (name, value, options) => { + actualCookieName = name; + actualCookieValue = value; + }) + + // when + + service.saveIfNotExist(); + + // then (instead of verify) + + expect(cookieServiceSpy.getCookie.calls.count()) + .toBe(1); + expect(cookieServiceSpy.setCookie.calls.count()) + .toBe(1); + expect(actualCookieName).toEqual(expectedCookieName); + + }); + + it('redirectAndClearIfExist', () => { + // given (instead of when) + + const url: string = 'some_url'; + + cookieServiceSpy.getCookie.and.returnValue(url); + + // when + + service.redirectAndClearIfExist(); + + // then (instead of verify) + + expect(cookieServiceSpy.getCookie.calls.count()) + .toBe(1); + expect(cookieServiceSpy.deleteCookie.calls.count()) + .toBe(1); + + expect(router.navigateByUrl.calls.count()) + .toBe(1); + + }); +}); + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/save-future-redirect.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/save-future-redirect.service.ts new file mode 100644 index 0000000..741c430 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/save-future-redirect.service.ts @@ -0,0 +1,38 @@ +import { Injectable } from '@angular/core'; +import { CookieService } from './cookie.service'; +import { Router } from '@angular/router'; + +@Injectable({ + providedIn: 'root' +}) +export class SaveFutureRedirectService { + + readonly urlCookieName = "CURRENT_URI"; + + constructor( + private cookieService: CookieService, + private router: Router + ) { } + + saveIfNotExist(){ + + if(!this.cookieService.getCookie(this.urlCookieName)){ + this.cookieService.setCookie(this.urlCookieName, this.router.url, { + path: '/', + secure: true, + samesite: 'lax' + }); + } + } + + redirectAndClearIfExist(){ + let url:string | undefined = this.cookieService.getCookie(this.urlCookieName); + + if(url){ + + this.cookieService.deleteCookie(this.urlCookieName); + + this.router.navigateByUrl(url); + } + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/window.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/window.service.ts new file mode 100644 index 0000000..c22c56a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/app/window.service.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class WindowService { + + get(): any{ + return window; + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/assets/.gitkeep b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/assets/cat_401.png b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/assets/cat_401.png new file mode 100644 index 0000000..08c7ff3 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/assets/cat_401.png differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/assets/error.jpg b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/assets/error.jpg new file mode 100644 index 0000000..5c162cd Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/assets/error.jpg differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/assets/logo.png b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/assets/logo.png new file mode 100644 index 0000000..0ef3fde Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/assets/logo.png differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/environments/environment.development.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/environments/environment.development.ts new file mode 100644 index 0000000..d66eb62 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/environments/environment.development.ts @@ -0,0 +1,5 @@ +export const environment = { + production: false, + apiUrl: 'http://localhost:3000', + authServerBaseUrl: '' +}; \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/environments/environment.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/environments/environment.ts new file mode 100644 index 0000000..28d6ae7 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/environments/environment.ts @@ -0,0 +1,5 @@ +export const environment = { + production: true, + apiUrl: '/api', + authServerBaseUrl: 'https://authorization-server.com' + }; \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/favicon.ico b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/favicon.ico new file mode 100644 index 0000000..ee799c9 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/favicon.ico differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz0dL_nz.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz0dL_nz.woff2 new file mode 100644 index 0000000..b52eb7a Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz0dL_nz.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz4dL_nz.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz4dL_nz.woff2 new file mode 100644 index 0000000..b038adc Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz4dL_nz.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2 new file mode 100644 index 0000000..327bebc Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzQdL_nz.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzQdL_nz.woff2 new file mode 100644 index 0000000..d63fc4a Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzQdL_nz.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc-CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc-CsTKlA.woff2 new file mode 100644 index 0000000..44e685b Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc-CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc0CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc0CsTKlA.woff2 new file mode 100644 index 0000000..a852d38 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc0CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc3CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc3CsTKlA.woff2 new file mode 100644 index 0000000..7eb322e Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc3CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc6CsQ.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc6CsQ.woff2 new file mode 100644 index 0000000..0ff2f81 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc6CsQ.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc-CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc-CsTKlA.woff2 new file mode 100644 index 0000000..7de8183 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc-CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.woff2 new file mode 100644 index 0000000..e2c6e1b Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc3CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc3CsTKlA.woff2 new file mode 100644 index 0000000..ccda5b9 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc3CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc6CsQ.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc6CsQ.woff2 new file mode 100644 index 0000000..7d846b1 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc6CsQ.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 new file mode 100644 index 0000000..7f69a29 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2 new file mode 100644 index 0000000..23e4e3c Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 new file mode 100644 index 0000000..d930de9 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2 new file mode 100644 index 0000000..b6653fb Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2 new file mode 100644 index 0000000..33a8438 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 new file mode 100644 index 0000000..0177669 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2 new file mode 100644 index 0000000..bb9e774 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 new file mode 100644 index 0000000..fe58be2 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2 new file mode 100644 index 0000000..4d9bb7d Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2 new file mode 100644 index 0000000..e9e3430 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxIIzI.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxIIzI.woff2 new file mode 100644 index 0000000..a5cc283 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxIIzI.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2 new file mode 100644 index 0000000..1758b7c Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2 new file mode 100644 index 0000000..d1d8a40 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 new file mode 100644 index 0000000..1862df5 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xIIzI.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xIIzI.woff2 new file mode 100644 index 0000000..2741d4f Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xIIzI.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 new file mode 100644 index 0000000..0171146 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2 new file mode 100644 index 0000000..1db8422 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 new file mode 100644 index 0000000..6362d7f Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2 new file mode 100644 index 0000000..b4b5df7 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 new file mode 100644 index 0000000..530e22c Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2 new file mode 100644 index 0000000..507809d Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 new file mode 100644 index 0000000..ef8c883 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2 new file mode 100644 index 0000000..8323edd Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 new file mode 100644 index 0000000..7d3dc27 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 new file mode 100644 index 0000000..6679743 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 new file mode 100644 index 0000000..32b25ee Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 new file mode 100644 index 0000000..7445d92 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2 new file mode 100644 index 0000000..5d7fed5 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2 new file mode 100644 index 0000000..4e8fac6 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfBBc4.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfBBc4.woff2 new file mode 100644 index 0000000..802499d Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfBBc4.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2 new file mode 100644 index 0000000..8f77a78 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2 new file mode 100644 index 0000000..9ddedcd Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu4mxK.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu4mxK.woff2 new file mode 100644 index 0000000..1a53701 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu4mxK.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 new file mode 100644 index 0000000..de226b3 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2 new file mode 100644 index 0000000..e3eabe7 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 new file mode 100644 index 0000000..de83f9c Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-brands-400.ttf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-brands-400.ttf new file mode 100644 index 0000000..774d51a Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-brands-400.ttf differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-brands-400.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-brands-400.woff2 new file mode 100644 index 0000000..71e3185 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-brands-400.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-regular-400.ttf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-regular-400.ttf new file mode 100644 index 0000000..8a9d634 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-regular-400.ttf differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-regular-400.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-regular-400.woff2 new file mode 100644 index 0000000..7f02168 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-regular-400.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-solid-900.ttf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-solid-900.ttf new file mode 100644 index 0000000..993dbe1 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-solid-900.ttf differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-solid-900.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-solid-900.woff2 new file mode 100644 index 0000000..5c16cd3 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-solid-900.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-v4compatibility.ttf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-v4compatibility.ttf new file mode 100644 index 0000000..ab6ae22 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-v4compatibility.ttf differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-v4compatibility.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-v4compatibility.woff2 new file mode 100644 index 0000000..9027e38 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/fonts/fontawesome-free/fa-v4compatibility.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/index.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/index.html new file mode 100644 index 0000000..fd555c4 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/index.html @@ -0,0 +1,13 @@ + + + + + Horns and hooves (readonly client) + + + + + + + + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/main.server.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/main.server.ts new file mode 100644 index 0000000..4b9d4d1 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/main.server.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { config } from './app/app.config.server'; + +const bootstrap = () => bootstrapApplication(AppComponent, config); + +export default bootstrap; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/main.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/main.ts new file mode 100644 index 0000000..35b00f3 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/main.ts @@ -0,0 +1,6 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { AppComponent } from './app/app.component'; + +bootstrapApplication(AppComponent, appConfig) + .catch((err) => console.error(err)); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/_shame.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/_shame.scss new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/base/_typography.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/base/_typography.scss new file mode 100644 index 0000000..0ed46af --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/base/_typography.scss @@ -0,0 +1,434 @@ + + +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'), url(/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz0dL_nz.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'), url(/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzQdL_nz.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'), url(/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz4dL_nz.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'), url(/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: local('Roboto Light Italic'), local('Roboto-LightItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: local('Roboto Light Italic'), local('Roboto-LightItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: local('Roboto Light Italic'), local('Roboto-LightItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: local('Roboto Light Italic'), local('Roboto-LightItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: local('Roboto Italic'), local('Roboto-Italic'), url(/fonts/Roboto/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: local('Roboto Italic'), local('Roboto-Italic'), url(/fonts/Roboto/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: local('Roboto Italic'), local('Roboto-Italic'), url(/fonts/Roboto/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: local('Roboto Italic'), local('Roboto-Italic'), url(/fonts/Roboto/KFOkCnqEu92Fr1Mu51xIIzI.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc-CsTKlA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: local('Roboto Black Italic'), local('Roboto-BlackItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: local('Roboto Black Italic'), local('Roboto-BlackItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc-CsTKlA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: local('Roboto Black Italic'), local('Roboto-BlackItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: local('Roboto Black Italic'), local('Roboto-BlackItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin'), local('Roboto-Thin'), url(/fonts/Roboto/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin'), local('Roboto-Thin'), url(/fonts/Roboto/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin'), local('Roboto-Thin'), url(/fonts/Roboto/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin'), local('Roboto-Thin'), url(/fonts/Roboto/KFOkCnqEu92Fr1MmgVxIIzI.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: local('Roboto Light'), local('Roboto-Light'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: local('Roboto Light'), local('Roboto-Light'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: local('Roboto Light'), local('Roboto-Light'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: local('Roboto Light'), local('Roboto-Light'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Roboto'), local('Roboto-Regular'), url(/fonts/Roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Roboto'), local('Roboto-Regular'), url(/fonts/Roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Roboto'), local('Roboto-Regular'), url(/fonts/Roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Roboto'), local('Roboto-Regular'), url(/fonts/Roboto/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium'), local('Roboto-Medium'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium'), local('Roboto-Medium'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium'), local('Roboto-Medium'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium'), local('Roboto-Medium'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold'), local('Roboto-Bold'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold'), local('Roboto-Bold'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold'), local('Roboto-Bold'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold'), local('Roboto-Bold'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: local('Roboto Black'), local('Roboto-Black'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: local('Roboto Black'), local('Roboto-Black'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: local('Roboto Black'), local('Roboto-Black'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: local('Roboto Black'), local('Roboto-Black'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/_material_io.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/_material_io.scss new file mode 100644 index 0000000..34b810e --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/_material_io.scss @@ -0,0 +1,36 @@ + +// Custom Theming for Angular Material +// For more information: https://material.angular.io/guide/theming +@use '@angular/material' as mat; +// Plus imports for other components in your app. + +// Include the common styles for Angular Material. We include this here so that you only +// have to load a single css file for Angular Material in your app. +// Be sure that you only ever include this mixin once! +@include mat.core(); + +// Define the palettes for your theme using the Material Design palettes available in palette.scss +// (imported above). For each palette, you can optionally specify a default, lighter, and darker +// hue. Available color palettes: https://material.io/design/color/ +$readonly-frontend-primary: mat.define-palette(mat.$indigo-palette); +$readonly-frontend-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); + +// The warn palette is optional (defaults to red). +$readonly-frontend-warn: mat.define-palette(mat.$red-palette); + +// Create the theme object. A theme consists of configurations for individual +// theming systems such as "color" or "typography". +$readonly-frontend-theme: mat.define-light-theme(( + color: ( + primary: $readonly-frontend-primary, + accent: $readonly-frontend-accent, + warn: $readonly-frontend-warn, + ) +)); + +// Include theme styles for core and each component used in your app. +// Alternatively, you can import and @include the theme mixins for each component +// that you are using. +@include mat.all-component-themes($readonly-frontend-theme); + +/* You can add global styles to this file, and also import other style files */ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_animated.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_animated.scss new file mode 100644 index 0000000..93555b2 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_animated.scss @@ -0,0 +1,153 @@ +// animating icons +// -------------------------- + +.#{$fa-css-prefix}-beat { + animation-name: #{$fa-css-prefix}-beat; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, ease-in-out); +} + +.#{$fa-css-prefix}-bounce { + animation-name: #{$fa-css-prefix}-bounce; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(0.280, 0.840, 0.420, 1)); +} + +.#{$fa-css-prefix}-fade { + animation-name: #{$fa-css-prefix}-fade; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(.4,0,.6,1)); +} + +.#{$fa-css-prefix}-beat-fade { + animation-name: #{$fa-css-prefix}-beat-fade; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(.4,0,.6,1)); +} + +.#{$fa-css-prefix}-flip { + animation-name: #{$fa-css-prefix}-flip; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, ease-in-out); +} + +.#{$fa-css-prefix}-shake { + animation-name: #{$fa-css-prefix}-shake; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, linear); +} + +.#{$fa-css-prefix}-spin { + animation-name: #{$fa-css-prefix}-spin; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 2s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, linear); +} + +.#{$fa-css-prefix}-spin-reverse { + --#{$fa-css-prefix}-animation-direction: reverse; +} + +.#{$fa-css-prefix}-pulse, +.#{$fa-css-prefix}-spin-pulse { + animation-name: #{$fa-css-prefix}-spin; + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, steps(8)); +} + +// if agent or operating system prefers reduced motion, disable animations +// see: https://www.smashingmagazine.com/2020/09/design-reduced-motion-sensitivities/ +// see: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion +@media (prefers-reduced-motion: reduce) { + .#{$fa-css-prefix}-beat, + .#{$fa-css-prefix}-bounce, + .#{$fa-css-prefix}-fade, + .#{$fa-css-prefix}-beat-fade, + .#{$fa-css-prefix}-flip, + .#{$fa-css-prefix}-pulse, + .#{$fa-css-prefix}-shake, + .#{$fa-css-prefix}-spin, + .#{$fa-css-prefix}-spin-pulse { + animation-delay: -1ms; + animation-duration: 1ms; + animation-iteration-count: 1; + transition-delay: 0s; + transition-duration: 0s; + } +} + +@keyframes #{$fa-css-prefix}-beat { + 0%, 90% { transform: scale(1); } + 45% { transform: scale(var(--#{$fa-css-prefix}-beat-scale, 1.25)); } +} + +@keyframes #{$fa-css-prefix}-bounce { + 0% { transform: scale(1,1) translateY(0); } + 10% { transform: scale(var(--#{$fa-css-prefix}-bounce-start-scale-x, 1.1),var(--#{$fa-css-prefix}-bounce-start-scale-y, 0.9)) translateY(0); } + 30% { transform: scale(var(--#{$fa-css-prefix}-bounce-jump-scale-x, 0.9),var(--#{$fa-css-prefix}-bounce-jump-scale-y, 1.1)) translateY(var(--#{$fa-css-prefix}-bounce-height, -0.5em)); } + 50% { transform: scale(var(--#{$fa-css-prefix}-bounce-land-scale-x, 1.05),var(--#{$fa-css-prefix}-bounce-land-scale-y, 0.95)) translateY(0); } + 57% { transform: scale(1,1) translateY(var(--#{$fa-css-prefix}-bounce-rebound, -0.125em)); } + 64% { transform: scale(1,1) translateY(0); } + 100% { transform: scale(1,1) translateY(0); } +} + +@keyframes #{$fa-css-prefix}-fade { + 50% { opacity: var(--#{$fa-css-prefix}-fade-opacity, 0.4); } +} + +@keyframes #{$fa-css-prefix}-beat-fade { + 0%, 100% { + opacity: var(--#{$fa-css-prefix}-beat-fade-opacity, 0.4); + transform: scale(1); + } + 50% { + opacity: 1; + transform: scale(var(--#{$fa-css-prefix}-beat-fade-scale, 1.125)); + } +} + +@keyframes #{$fa-css-prefix}-flip { + 50% { + transform: rotate3d(var(--#{$fa-css-prefix}-flip-x, 0), var(--#{$fa-css-prefix}-flip-y, 1), var(--#{$fa-css-prefix}-flip-z, 0), var(--#{$fa-css-prefix}-flip-angle, -180deg)); + } +} + +@keyframes #{$fa-css-prefix}-shake { + 0% { transform: rotate(-15deg); } + 4% { transform: rotate(15deg); } + 8%, 24% { transform: rotate(-18deg); } + 12%, 28% { transform: rotate(18deg); } + 16% { transform: rotate(-22deg); } + 20% { transform: rotate(22deg); } + 32% { transform: rotate(-12deg); } + 36% { transform: rotate(12deg); } + 40%, 100% { transform: rotate(0deg); } +} + +@keyframes #{$fa-css-prefix}-spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_bordered-pulled.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_bordered-pulled.scss new file mode 100644 index 0000000..9068253 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_bordered-pulled.scss @@ -0,0 +1,20 @@ +// bordered + pulled icons +// ------------------------- + +.#{$fa-css-prefix}-border { + border-color: var(--#{$fa-css-prefix}-border-color, #{$fa-border-color}); + border-radius: var(--#{$fa-css-prefix}-border-radius, #{$fa-border-radius}); + border-style: var(--#{$fa-css-prefix}-border-style, #{$fa-border-style}); + border-width: var(--#{$fa-css-prefix}-border-width, #{$fa-border-width}); + padding: var(--#{$fa-css-prefix}-border-padding, #{$fa-border-padding}); +} + +.#{$fa-css-prefix}-pull-left { + float: left; + margin-right: var(--#{$fa-css-prefix}-pull-margin, #{$fa-pull-margin}); +} + +.#{$fa-css-prefix}-pull-right { + float: right; + margin-left: var(--#{$fa-css-prefix}-pull-margin, #{$fa-pull-margin}); +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_core.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_core.scss new file mode 100644 index 0000000..1b2fd99 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_core.scss @@ -0,0 +1,43 @@ +// base icon class definition +// ------------------------- + +.#{$fa-css-prefix} { + font-family: var(--#{$fa-css-prefix}-style-family, '#{$fa-style-family}'); + font-weight: var(--#{$fa-css-prefix}-style, #{$fa-style}); +} + +.#{$fa-css-prefix}, +.#{$fa-css-prefix}-classic, +.#{$fa-css-prefix}-sharp, +.fas, +.#{$fa-css-prefix}-solid, +.far, +.#{$fa-css-prefix}-regular, +.fab, +.#{$fa-css-prefix}-brands { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: var(--#{$fa-css-prefix}-display, #{$fa-display}); + font-style: normal; + font-variant: normal; + line-height: 1; + text-rendering: auto; +} + +.fas, +.#{$fa-css-prefix}-classic, +.#{$fa-css-prefix}-solid, +.far, +.#{$fa-css-prefix}-regular { + font-family: 'Font Awesome 6 Free'; +} + +.fab, +.#{$fa-css-prefix}-brands { + font-family: 'Font Awesome 6 Brands'; +} + + +%fa-icon { + @include fa-icon; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_fixed-width.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_fixed-width.scss new file mode 100644 index 0000000..7234236 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_fixed-width.scss @@ -0,0 +1,7 @@ +// fixed-width icons +// ------------------------- + +.#{$fa-css-prefix}-fw { + text-align: center; + width: $fa-fw-width; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_functions.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_functions.scss new file mode 100644 index 0000000..a17ffe8 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_functions.scss @@ -0,0 +1,57 @@ +// functions +// -------------------------- + +// fa-content: convenience function used to set content property +@function fa-content($fa-var) { + @return unquote("\"#{ $fa-var }\""); +} + +// fa-divide: Originally obtained from the Bootstrap https://github.com/twbs/bootstrap +// +// Licensed under: The MIT License (MIT) +// +// Copyright (c) 2011-2021 Twitter, Inc. +// Copyright (c) 2011-2021 The Bootstrap Authors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +@function fa-divide($dividend, $divisor, $precision: 10) { + $sign: if($dividend > 0 and $divisor > 0, 1, -1); + $dividend: abs($dividend); + $divisor: abs($divisor); + $quotient: 0; + $remainder: $dividend; + @if $dividend == 0 { + @return 0; + } + @if $divisor == 0 { + @error "Cannot divide by 0"; + } + @if $divisor == 1 { + @return $dividend; + } + @while $remainder >= $divisor { + $quotient: $quotient + 1; + $remainder: $remainder - $divisor; + } + @if $remainder > 0 and $precision > 0 { + $remainder: fa-divide($remainder * 10, $divisor, $precision - 1) * .1; + } + @return ($quotient + $remainder) * $sign; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_icons.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_icons.scss new file mode 100644 index 0000000..0f55926 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_icons.scss @@ -0,0 +1,10 @@ +// specific icon class definition +// ------------------------- + +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen +readers do not read off random characters that represent icons */ + +@each $name, $icon in $fa-icons { + .#{$fa-css-prefix}-#{$name}::before { content: unquote("\"#{ $icon }\""); } +} + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_list.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_list.scss new file mode 100644 index 0000000..ced36e2 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_list.scss @@ -0,0 +1,18 @@ +// icons in a list +// ------------------------- + +.#{$fa-css-prefix}-ul { + list-style-type: none; + margin-left: var(--#{$fa-css-prefix}-li-margin, #{$fa-li-margin}); + padding-left: 0; + + > li { position: relative; } +} + +.#{$fa-css-prefix}-li { + left: calc(var(--#{$fa-css-prefix}-li-width, #{$fa-li-width}) * -1); + position: absolute; + text-align: center; + width: var(--#{$fa-css-prefix}-li-width, #{$fa-li-width}); + line-height: inherit; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_mixins.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_mixins.scss new file mode 100644 index 0000000..e06b69a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_mixins.scss @@ -0,0 +1,75 @@ +// mixins +// -------------------------- + +// base rendering for an icon +@mixin fa-icon { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + display: inline-block; + font-style: normal; + font-variant: normal; + font-weight: normal; + line-height: 1; +} + +// sets relative font-sizing and alignment (in _sizing) +@mixin fa-size ($font-size) { + font-size: fa-divide($font-size, $fa-size-scale-base) * 1em; // converts step in sizing scale into an em-based value that's relative to the scale's base + line-height: fa-divide(1, $font-size) * 1em; // sets the line-height of the icon back to that of it's parent + vertical-align: (fa-divide(6, $font-size) - fa-divide(3, 8)) * 1em; // vertically centers the icon taking into account the surrounding text's descender +} + +// only display content to screen readers +// see: https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/ +// see: https://hugogiraudel.com/2016/10/13/css-hide-and-seek/ +@mixin fa-sr-only() { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +// use in conjunction with .sr-only to only display content when it's focused +@mixin fa-sr-only-focusable() { + &:not(:focus) { + @include fa-sr-only(); + } +} + +// sets a specific icon family to use alongside style + icon mixins + +// convenience mixins for declaring pseudo-elements by CSS variable, +// including all style-specific font properties, and both the ::before +// and ::after elements in the duotone case. +@mixin fa-icon-solid($fa-var) { + @extend %fa-icon; + @extend .fa-solid; + + &::before { + content: unquote("\"#{ $fa-var }\""); + } +} + +@mixin fa-icon-regular($fa-var) { + @extend %fa-icon; + @extend .fa-regular; + + &::before { + content: unquote("\"#{ $fa-var }\""); + } +} + +@mixin fa-icon-brands($fa-var) { + @extend %fa-icon; + @extend .fa-brands; + + &::before { + content: unquote("\"#{ $fa-var }\""); + } +} + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_rotated-flipped.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_rotated-flipped.scss new file mode 100644 index 0000000..f27fabe --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_rotated-flipped.scss @@ -0,0 +1,31 @@ +// rotating + flipping icons +// ------------------------- + +.#{$fa-css-prefix}-rotate-90 { + transform: rotate(90deg); +} + +.#{$fa-css-prefix}-rotate-180 { + transform: rotate(180deg); +} + +.#{$fa-css-prefix}-rotate-270 { + transform: rotate(270deg); +} + +.#{$fa-css-prefix}-flip-horizontal { + transform: scale(-1, 1); +} + +.#{$fa-css-prefix}-flip-vertical { + transform: scale(1, -1); +} + +.#{$fa-css-prefix}-flip-both, +.#{$fa-css-prefix}-flip-horizontal.#{$fa-css-prefix}-flip-vertical { + transform: scale(-1, -1); +} + +.#{$fa-css-prefix}-rotate-by { + transform: rotate(var(--#{$fa-css-prefix}-rotate-angle, none)); +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_screen-reader.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_screen-reader.scss new file mode 100644 index 0000000..2beb887 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_screen-reader.scss @@ -0,0 +1,14 @@ +// screen-reader utilities +// ------------------------- + +// only display content to screen readers +.sr-only, +.#{$fa-css-prefix}-sr-only { + @include fa-sr-only; +} + +// use in conjunction with .sr-only to only display content when it's focused +.sr-only-focusable, +.#{$fa-css-prefix}-sr-only-focusable { + @include fa-sr-only-focusable; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_shims.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_shims.scss new file mode 100644 index 0000000..7809aa6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_shims.scss @@ -0,0 +1,2042 @@ +.#{$fa-css-prefix}.#{$fa-css-prefix}-glass:before { content: unquote("\"#{ $fa-var-martini-glass-empty }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-o:before { content: unquote("\"#{ $fa-var-envelope }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-o:before { content: unquote("\"#{ $fa-var-star }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-remove:before { content: unquote("\"#{ $fa-var-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-close:before { content: unquote("\"#{ $fa-var-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gear:before { content: unquote("\"#{ $fa-var-gear }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-trash-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-trash-o:before { content: unquote("\"#{ $fa-var-trash-can }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-home:before { content: unquote("\"#{ $fa-var-house }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-o:before { content: unquote("\"#{ $fa-var-file }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-clock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-clock-o:before { content: unquote("\"#{ $fa-var-clock }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-down:before { content: unquote("\"#{ $fa-var-circle-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-up:before { content: unquote("\"#{ $fa-var-circle-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-play-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-play-circle-o:before { content: unquote("\"#{ $fa-var-circle-play }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-repeat:before { content: unquote("\"#{ $fa-var-arrow-rotate-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rotate-right:before { content: unquote("\"#{ $fa-var-arrow-rotate-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-refresh:before { content: unquote("\"#{ $fa-var-arrows-rotate }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-list-alt { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-list-alt:before { content: unquote("\"#{ $fa-var-rectangle-list }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dedent:before { content: unquote("\"#{ $fa-var-outdent }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-video-camera:before { content: unquote("\"#{ $fa-var-video }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-picture-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-picture-o:before { content: unquote("\"#{ $fa-var-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-photo { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-photo:before { content: unquote("\"#{ $fa-var-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-image { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-image:before { content: unquote("\"#{ $fa-var-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-map-marker:before { content: unquote("\"#{ $fa-var-location-dot }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pencil-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pencil-square-o:before { content: unquote("\"#{ $fa-var-pen-to-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-edit { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-edit:before { content: unquote("\"#{ $fa-var-pen-to-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-share-square-o:before { content: unquote("\"#{ $fa-var-share-from-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-square-o:before { content: unquote("\"#{ $fa-var-square-check }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows:before { content: unquote("\"#{ $fa-var-up-down-left-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-circle-o:before { content: unquote("\"#{ $fa-var-circle-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-circle-o:before { content: unquote("\"#{ $fa-var-circle-check }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mail-forward:before { content: unquote("\"#{ $fa-var-share }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-expand:before { content: unquote("\"#{ $fa-var-up-right-and-down-left-from-center }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-compress:before { content: unquote("\"#{ $fa-var-down-left-and-up-right-to-center }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eye { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eye-slash { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-warning:before { content: unquote("\"#{ $fa-var-triangle-exclamation }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar:before { content: unquote("\"#{ $fa-var-calendar-days }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows-v:before { content: unquote("\"#{ $fa-var-up-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows-h:before { content: unquote("\"#{ $fa-var-left-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bar-chart:before { content: unquote("\"#{ $fa-var-chart-column }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bar-chart-o:before { content: unquote("\"#{ $fa-var-chart-column }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitter-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitter-square:before { content: unquote("\"#{ $fa-var-square-twitter }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-square:before { content: unquote("\"#{ $fa-var-square-facebook }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gears:before { content: unquote("\"#{ $fa-var-gears }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-up:before { content: unquote("\"#{ $fa-var-thumbs-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-down:before { content: unquote("\"#{ $fa-var-thumbs-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-heart-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-heart-o:before { content: unquote("\"#{ $fa-var-heart }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sign-out:before { content: unquote("\"#{ $fa-var-right-from-bracket }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin-square:before { content: unquote("\"#{ $fa-var-linkedin }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumb-tack:before { content: unquote("\"#{ $fa-var-thumbtack }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-external-link:before { content: unquote("\"#{ $fa-var-up-right-from-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sign-in:before { content: unquote("\"#{ $fa-var-right-to-bracket }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-github-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-github-square:before { content: unquote("\"#{ $fa-var-square-github }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-lemon-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-lemon-o:before { content: unquote("\"#{ $fa-var-lemon }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-square-o:before { content: unquote("\"#{ $fa-var-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bookmark-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bookmark-o:before { content: unquote("\"#{ $fa-var-bookmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitter { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook:before { content: unquote("\"#{ $fa-var-facebook-f }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-f { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-f:before { content: unquote("\"#{ $fa-var-facebook-f }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-github { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-credit-card { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-feed:before { content: unquote("\"#{ $fa-var-rss }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hdd-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hdd-o:before { content: unquote("\"#{ $fa-var-hard-drive }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-right:before { content: unquote("\"#{ $fa-var-hand-point-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-left:before { content: unquote("\"#{ $fa-var-hand-point-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-up:before { content: unquote("\"#{ $fa-var-hand-point-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-down:before { content: unquote("\"#{ $fa-var-hand-point-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-globe:before { content: unquote("\"#{ $fa-var-earth-americas }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tasks:before { content: unquote("\"#{ $fa-var-bars-progress }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows-alt:before { content: unquote("\"#{ $fa-var-maximize }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-group:before { content: unquote("\"#{ $fa-var-users }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-chain:before { content: unquote("\"#{ $fa-var-link }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cut:before { content: unquote("\"#{ $fa-var-scissors }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-files-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-files-o:before { content: unquote("\"#{ $fa-var-copy }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-floppy-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-floppy-o:before { content: unquote("\"#{ $fa-var-floppy-disk }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-save { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-save:before { content: unquote("\"#{ $fa-var-floppy-disk }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-navicon:before { content: unquote("\"#{ $fa-var-bars }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-reorder:before { content: unquote("\"#{ $fa-var-bars }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-magic:before { content: unquote("\"#{ $fa-var-wand-magic-sparkles }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest-square:before { content: unquote("\"#{ $fa-var-square-pinterest }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-square:before { content: unquote("\"#{ $fa-var-square-google-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus:before { content: unquote("\"#{ $fa-var-google-plus-g }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-money:before { content: unquote("\"#{ $fa-var-money-bill-1 }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-unsorted:before { content: unquote("\"#{ $fa-var-sort }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-desc:before { content: unquote("\"#{ $fa-var-sort-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-asc:before { content: unquote("\"#{ $fa-var-sort-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin:before { content: unquote("\"#{ $fa-var-linkedin-in }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rotate-left:before { content: unquote("\"#{ $fa-var-arrow-rotate-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-legal:before { content: unquote("\"#{ $fa-var-gavel }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tachometer:before { content: unquote("\"#{ $fa-var-gauge-high }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dashboard:before { content: unquote("\"#{ $fa-var-gauge-high }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-comment-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-comment-o:before { content: unquote("\"#{ $fa-var-comment }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-comments-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-comments-o:before { content: unquote("\"#{ $fa-var-comments }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-flash:before { content: unquote("\"#{ $fa-var-bolt }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-clipboard:before { content: unquote("\"#{ $fa-var-paste }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-lightbulb-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-lightbulb-o:before { content: unquote("\"#{ $fa-var-lightbulb }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-exchange:before { content: unquote("\"#{ $fa-var-right-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cloud-download:before { content: unquote("\"#{ $fa-var-cloud-arrow-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cloud-upload:before { content: unquote("\"#{ $fa-var-cloud-arrow-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-o:before { content: unquote("\"#{ $fa-var-bell }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cutlery:before { content: unquote("\"#{ $fa-var-utensils }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-text-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-text-o:before { content: unquote("\"#{ $fa-var-file-lines }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-building-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-building-o:before { content: unquote("\"#{ $fa-var-building }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hospital-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hospital-o:before { content: unquote("\"#{ $fa-var-hospital }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tablet:before { content: unquote("\"#{ $fa-var-tablet-screen-button }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mobile:before { content: unquote("\"#{ $fa-var-mobile-screen-button }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mobile-phone:before { content: unquote("\"#{ $fa-var-mobile-screen-button }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-o:before { content: unquote("\"#{ $fa-var-circle }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mail-reply:before { content: unquote("\"#{ $fa-var-reply }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-github-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-o:before { content: unquote("\"#{ $fa-var-folder }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-open-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-open-o:before { content: unquote("\"#{ $fa-var-folder-open }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-smile-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-smile-o:before { content: unquote("\"#{ $fa-var-face-smile }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-frown-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-frown-o:before { content: unquote("\"#{ $fa-var-face-frown }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-meh-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-meh-o:before { content: unquote("\"#{ $fa-var-face-meh }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-keyboard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-keyboard-o:before { content: unquote("\"#{ $fa-var-keyboard }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-flag-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-flag-o:before { content: unquote("\"#{ $fa-var-flag }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mail-reply-all:before { content: unquote("\"#{ $fa-var-reply-all }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-o:before { content: unquote("\"#{ $fa-var-star-half-stroke }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-empty { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-empty:before { content: unquote("\"#{ $fa-var-star-half-stroke }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-full { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-full:before { content: unquote("\"#{ $fa-var-star-half-stroke }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-code-fork:before { content: unquote("\"#{ $fa-var-code-branch }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-chain-broken:before { content: unquote("\"#{ $fa-var-link-slash }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-unlink:before { content: unquote("\"#{ $fa-var-link-slash }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-o:before { content: unquote("\"#{ $fa-var-calendar }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-maxcdn { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-html5 { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-css3 { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-unlock-alt:before { content: unquote("\"#{ $fa-var-unlock }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-minus-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-minus-square-o:before { content: unquote("\"#{ $fa-var-square-minus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-level-up:before { content: unquote("\"#{ $fa-var-turn-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-level-down:before { content: unquote("\"#{ $fa-var-turn-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pencil-square:before { content: unquote("\"#{ $fa-var-square-pen }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-external-link-square:before { content: unquote("\"#{ $fa-var-square-up-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-compass { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-down:before { content: unquote("\"#{ $fa-var-square-caret-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-down:before { content: unquote("\"#{ $fa-var-square-caret-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-up:before { content: unquote("\"#{ $fa-var-square-caret-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-up:before { content: unquote("\"#{ $fa-var-square-caret-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-right:before { content: unquote("\"#{ $fa-var-square-caret-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-right:before { content: unquote("\"#{ $fa-var-square-caret-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eur:before { content: unquote("\"#{ $fa-var-euro-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-euro:before { content: unquote("\"#{ $fa-var-euro-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gbp:before { content: unquote("\"#{ $fa-var-sterling-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-usd:before { content: unquote("\"#{ $fa-var-dollar-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dollar:before { content: unquote("\"#{ $fa-var-dollar-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-inr:before { content: unquote("\"#{ $fa-var-indian-rupee-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rupee:before { content: unquote("\"#{ $fa-var-indian-rupee-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-jpy:before { content: unquote("\"#{ $fa-var-yen-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cny:before { content: unquote("\"#{ $fa-var-yen-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rmb:before { content: unquote("\"#{ $fa-var-yen-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yen:before { content: unquote("\"#{ $fa-var-yen-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rub:before { content: unquote("\"#{ $fa-var-ruble-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ruble:before { content: unquote("\"#{ $fa-var-ruble-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rouble:before { content: unquote("\"#{ $fa-var-ruble-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-krw:before { content: unquote("\"#{ $fa-var-won-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-won:before { content: unquote("\"#{ $fa-var-won-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-btc { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitcoin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitcoin:before { content: unquote("\"#{ $fa-var-btc }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-text:before { content: unquote("\"#{ $fa-var-file-lines }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-alpha-asc:before { content: unquote("\"#{ $fa-var-arrow-down-a-z }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-alpha-desc:before { content: unquote("\"#{ $fa-var-arrow-down-z-a }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-amount-asc:before { content: unquote("\"#{ $fa-var-arrow-down-short-wide }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-amount-desc:before { content: unquote("\"#{ $fa-var-arrow-down-wide-short }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-numeric-asc:before { content: unquote("\"#{ $fa-var-arrow-down-1-9 }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-numeric-desc:before { content: unquote("\"#{ $fa-var-arrow-down-9-1 }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-square:before { content: unquote("\"#{ $fa-var-square-youtube }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-xing { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-xing-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-xing-square:before { content: unquote("\"#{ $fa-var-square-xing }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-play { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-play:before { content: unquote("\"#{ $fa-var-youtube }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dropbox { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stack-overflow { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-instagram { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-flickr { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-adn { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitbucket { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitbucket-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitbucket-square:before { content: unquote("\"#{ $fa-var-bitbucket }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tumblr { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tumblr-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-tumblr-square:before { content: unquote("\"#{ $fa-var-square-tumblr }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-down:before { content: unquote("\"#{ $fa-var-down-long }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-up:before { content: unquote("\"#{ $fa-var-up-long }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-left:before { content: unquote("\"#{ $fa-var-left-long }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-right:before { content: unquote("\"#{ $fa-var-right-long }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-apple { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-windows { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-android { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-linux { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dribbble { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-skype { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-foursquare { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-trello { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gratipay { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gittip { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-gittip:before { content: unquote("\"#{ $fa-var-gratipay }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sun-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-sun-o:before { content: unquote("\"#{ $fa-var-sun }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-moon-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-moon-o:before { content: unquote("\"#{ $fa-var-moon }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vk { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-weibo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-renren { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pagelines { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stack-exchange { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-right:before { content: unquote("\"#{ $fa-var-circle-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-left:before { content: unquote("\"#{ $fa-var-circle-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-left:before { content: unquote("\"#{ $fa-var-square-caret-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-left:before { content: unquote("\"#{ $fa-var-square-caret-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dot-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-dot-circle-o:before { content: unquote("\"#{ $fa-var-circle-dot }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo-square:before { content: unquote("\"#{ $fa-var-square-vimeo }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-try:before { content: unquote("\"#{ $fa-var-turkish-lira-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-turkish-lira:before { content: unquote("\"#{ $fa-var-turkish-lira-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-plus-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-plus-square-o:before { content: unquote("\"#{ $fa-var-square-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-slack { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wordpress { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-openid { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-institution:before { content: unquote("\"#{ $fa-var-building-columns }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bank:before { content: unquote("\"#{ $fa-var-building-columns }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mortar-board:before { content: unquote("\"#{ $fa-var-graduation-cap }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yahoo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit-square:before { content: unquote("\"#{ $fa-var-square-reddit }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stumbleupon-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stumbleupon { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-delicious { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-digg { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pied-piper-pp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pied-piper-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-drupal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-joomla { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-behance { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-behance-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-behance-square:before { content: unquote("\"#{ $fa-var-square-behance }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-steam { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-steam-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-steam-square:before { content: unquote("\"#{ $fa-var-square-steam }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-automobile:before { content: unquote("\"#{ $fa-var-car }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cab:before { content: unquote("\"#{ $fa-var-taxi }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-spotify { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-deviantart { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-soundcloud { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-pdf-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-pdf-o:before { content: unquote("\"#{ $fa-var-file-pdf }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-word-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-word-o:before { content: unquote("\"#{ $fa-var-file-word }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-excel-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-excel-o:before { content: unquote("\"#{ $fa-var-file-excel }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-powerpoint-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-powerpoint-o:before { content: unquote("\"#{ $fa-var-file-powerpoint }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-image-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-image-o:before { content: unquote("\"#{ $fa-var-file-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-photo-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-photo-o:before { content: unquote("\"#{ $fa-var-file-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-picture-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-picture-o:before { content: unquote("\"#{ $fa-var-file-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-archive-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-archive-o:before { content: unquote("\"#{ $fa-var-file-zipper }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-zip-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-zip-o:before { content: unquote("\"#{ $fa-var-file-zipper }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-audio-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-audio-o:before { content: unquote("\"#{ $fa-var-file-audio }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-sound-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-sound-o:before { content: unquote("\"#{ $fa-var-file-audio }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-video-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-video-o:before { content: unquote("\"#{ $fa-var-file-video }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-movie-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-movie-o:before { content: unquote("\"#{ $fa-var-file-video }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-code-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-code-o:before { content: unquote("\"#{ $fa-var-file-code }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vine { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-codepen { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-jsfiddle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-life-bouy:before { content: unquote("\"#{ $fa-var-life-ring }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-life-buoy:before { content: unquote("\"#{ $fa-var-life-ring }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-life-saver:before { content: unquote("\"#{ $fa-var-life-ring }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-support:before { content: unquote("\"#{ $fa-var-life-ring }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-o-notch:before { content: unquote("\"#{ $fa-var-circle-notch }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rebel { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ra { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-ra:before { content: unquote("\"#{ $fa-var-rebel }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-resistance { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-resistance:before { content: unquote("\"#{ $fa-var-rebel }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-empire { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ge { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-ge:before { content: unquote("\"#{ $fa-var-empire }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-git-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-git-square:before { content: unquote("\"#{ $fa-var-square-git }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-git { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hacker-news { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-y-combinator-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-y-combinator-square:before { content: unquote("\"#{ $fa-var-hacker-news }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc-square:before { content: unquote("\"#{ $fa-var-hacker-news }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tencent-weibo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-qq { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-weixin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wechat { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wechat:before { content: unquote("\"#{ $fa-var-weixin }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-send:before { content: unquote("\"#{ $fa-var-paper-plane }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-paper-plane-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-paper-plane-o:before { content: unquote("\"#{ $fa-var-paper-plane }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-send-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-send-o:before { content: unquote("\"#{ $fa-var-paper-plane }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-thin { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-thin:before { content: unquote("\"#{ $fa-var-circle }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-header:before { content: unquote("\"#{ $fa-var-heading }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-futbol-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-futbol-o:before { content: unquote("\"#{ $fa-var-futbol }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-soccer-ball-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-soccer-ball-o:before { content: unquote("\"#{ $fa-var-futbol }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-slideshare { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitch { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yelp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-newspaper-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-newspaper-o:before { content: unquote("\"#{ $fa-var-newspaper }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-paypal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-wallet { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-visa { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-mastercard { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-discover { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-amex { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-paypal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-stripe { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-slash-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-slash-o:before { content: unquote("\"#{ $fa-var-bell-slash }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-trash:before { content: unquote("\"#{ $fa-var-trash-can }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-copyright { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eyedropper:before { content: unquote("\"#{ $fa-var-eye-dropper }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-area-chart:before { content: unquote("\"#{ $fa-var-chart-area }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pie-chart:before { content: unquote("\"#{ $fa-var-chart-pie }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-line-chart:before { content: unquote("\"#{ $fa-var-chart-line }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-lastfm { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-lastfm-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-lastfm-square:before { content: unquote("\"#{ $fa-var-square-lastfm }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ioxhost { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-angellist { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc:before { content: unquote("\"#{ $fa-var-closed-captioning }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ils:before { content: unquote("\"#{ $fa-var-shekel-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-shekel:before { content: unquote("\"#{ $fa-var-shekel-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sheqel:before { content: unquote("\"#{ $fa-var-shekel-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-buysellads { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-connectdevelop { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dashcube { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-forumbee { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-leanpub { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sellsy { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-shirtsinbulk { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-simplybuilt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-skyatlas { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-diamond { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-diamond:before { content: unquote("\"#{ $fa-var-gem }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-transgender:before { content: unquote("\"#{ $fa-var-mars-and-venus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-intersex:before { content: unquote("\"#{ $fa-var-mars-and-venus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-transgender-alt:before { content: unquote("\"#{ $fa-var-transgender }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-official { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-official:before { content: unquote("\"#{ $fa-var-facebook }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest-p { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-whatsapp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hotel:before { content: unquote("\"#{ $fa-var-bed }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-viacoin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-medium { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-y-combinator { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc:before { content: unquote("\"#{ $fa-var-y-combinator }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-optin-monster { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-opencart { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-expeditedssl { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-4:before { content: unquote("\"#{ $fa-var-battery-full }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery:before { content: unquote("\"#{ $fa-var-battery-full }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-3:before { content: unquote("\"#{ $fa-var-battery-three-quarters }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-2:before { content: unquote("\"#{ $fa-var-battery-half }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-1:before { content: unquote("\"#{ $fa-var-battery-quarter }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-0:before { content: unquote("\"#{ $fa-var-battery-empty }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-object-group { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-object-ungroup { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sticky-note-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-sticky-note-o:before { content: unquote("\"#{ $fa-var-note-sticky }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-jcb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-diners-club { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-clone { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-o:before { content: unquote("\"#{ $fa-var-hourglass }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-1:before { content: unquote("\"#{ $fa-var-hourglass-start }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-2:before { content: unquote("\"#{ $fa-var-hourglass-half }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-3:before { content: unquote("\"#{ $fa-var-hourglass-end }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-rock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-rock-o:before { content: unquote("\"#{ $fa-var-hand-back-fist }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-grab-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-grab-o:before { content: unquote("\"#{ $fa-var-hand-back-fist }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-paper-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-paper-o:before { content: unquote("\"#{ $fa-var-hand }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-stop-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-stop-o:before { content: unquote("\"#{ $fa-var-hand }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-scissors-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-scissors-o:before { content: unquote("\"#{ $fa-var-hand-scissors }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-lizard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-lizard-o:before { content: unquote("\"#{ $fa-var-hand-lizard }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-spock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-spock-o:before { content: unquote("\"#{ $fa-var-hand-spock }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-pointer-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-pointer-o:before { content: unquote("\"#{ $fa-var-hand-pointer }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-peace-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-peace-o:before { content: unquote("\"#{ $fa-var-hand-peace }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-registered { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-creative-commons { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gg { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gg-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-odnoklassniki { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-odnoklassniki-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-odnoklassniki-square:before { content: unquote("\"#{ $fa-var-square-odnoklassniki }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-get-pocket { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wikipedia-w { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-safari { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-chrome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-firefox { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-opera { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-internet-explorer { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-television:before { content: unquote("\"#{ $fa-var-tv }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-contao { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-500px { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-amazon { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-plus-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-plus-o:before { content: unquote("\"#{ $fa-var-calendar-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-minus-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-minus-o:before { content: unquote("\"#{ $fa-var-calendar-minus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-times-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-times-o:before { content: unquote("\"#{ $fa-var-calendar-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-check-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-check-o:before { content: unquote("\"#{ $fa-var-calendar-check }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-map-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-map-o:before { content: unquote("\"#{ $fa-var-map }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-commenting:before { content: unquote("\"#{ $fa-var-comment-dots }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-commenting-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-commenting-o:before { content: unquote("\"#{ $fa-var-comment-dots }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-houzz { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo:before { content: unquote("\"#{ $fa-var-vimeo-v }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-black-tie { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-fonticons { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit-alien { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-edge { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-credit-card-alt:before { content: unquote("\"#{ $fa-var-credit-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-codiepie { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-modx { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-fort-awesome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-usb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-product-hunt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mixcloud { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-scribd { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pause-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pause-circle-o:before { content: unquote("\"#{ $fa-var-circle-pause }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stop-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-stop-circle-o:before { content: unquote("\"#{ $fa-var-circle-stop }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bluetooth { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bluetooth-b { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gitlab { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wpbeginner { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wpforms { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-envira { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wheelchair-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wheelchair-alt:before { content: unquote("\"#{ $fa-var-accessible-icon }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-question-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-question-circle-o:before { content: unquote("\"#{ $fa-var-circle-question }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-volume-control-phone:before { content: unquote("\"#{ $fa-var-phone-volume }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-asl-interpreting:before { content: unquote("\"#{ $fa-var-hands-asl-interpreting }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-deafness:before { content: unquote("\"#{ $fa-var-ear-deaf }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hard-of-hearing:before { content: unquote("\"#{ $fa-var-ear-deaf }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-glide { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-glide-g { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-signing:before { content: unquote("\"#{ $fa-var-hands }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-viadeo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-viadeo-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-viadeo-square:before { content: unquote("\"#{ $fa-var-square-viadeo }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-ghost { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-ghost:before { content: unquote("\"#{ $fa-var-snapchat }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-square:before { content: unquote("\"#{ $fa-var-square-snapchat }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pied-piper { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-first-order { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yoast { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-themeisle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-official { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-official:before { content: unquote("\"#{ $fa-var-google-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-circle:before { content: unquote("\"#{ $fa-var-google-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-font-awesome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-fa { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-fa:before { content: unquote("\"#{ $fa-var-font-awesome }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-handshake-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-handshake-o:before { content: unquote("\"#{ $fa-var-handshake }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-open-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-open-o:before { content: unquote("\"#{ $fa-var-envelope-open }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-linode { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-book-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-book-o:before { content: unquote("\"#{ $fa-var-address-book }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vcard:before { content: unquote("\"#{ $fa-var-address-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-card-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-card-o:before { content: unquote("\"#{ $fa-var-address-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vcard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-vcard-o:before { content: unquote("\"#{ $fa-var-address-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-circle-o:before { content: unquote("\"#{ $fa-var-circle-user }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-o:before { content: unquote("\"#{ $fa-var-user }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-id-badge { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-drivers-license:before { content: unquote("\"#{ $fa-var-id-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-id-card-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-id-card-o:before { content: unquote("\"#{ $fa-var-id-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-drivers-license-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-drivers-license-o:before { content: unquote("\"#{ $fa-var-id-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-quora { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-free-code-camp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-telegram { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-4:before { content: unquote("\"#{ $fa-var-temperature-full }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer:before { content: unquote("\"#{ $fa-var-temperature-full }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-3:before { content: unquote("\"#{ $fa-var-temperature-three-quarters }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-2:before { content: unquote("\"#{ $fa-var-temperature-half }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-1:before { content: unquote("\"#{ $fa-var-temperature-quarter }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-0:before { content: unquote("\"#{ $fa-var-temperature-empty }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bathtub:before { content: unquote("\"#{ $fa-var-bath }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-s15:before { content: unquote("\"#{ $fa-var-bath }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-maximize { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-restore { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-rectangle:before { content: unquote("\"#{ $fa-var-rectangle-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-close-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-close-o:before { content: unquote("\"#{ $fa-var-rectangle-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-rectangle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-rectangle-o:before { content: unquote("\"#{ $fa-var-rectangle-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bandcamp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-grav { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-etsy { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-imdb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ravelry { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eercast { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-eercast:before { content: unquote("\"#{ $fa-var-sellcast }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-snowflake-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-snowflake-o:before { content: unquote("\"#{ $fa-var-snowflake }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-superpowers { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wpexplorer { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-meetup { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_sizing.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_sizing.scss new file mode 100644 index 0000000..e171e7d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_sizing.scss @@ -0,0 +1,16 @@ +// sizing icons +// ------------------------- + +// literal magnification scale +@for $i from 1 through 10 { + .#{$fa-css-prefix}-#{$i}x { + font-size: $i * 1em; + } +} + +// step-based scale (with alignment) +@each $size, $value in $fa-sizes { + .#{$fa-css-prefix}-#{$size} { + @include fa-size($value); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_stacked.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_stacked.scss new file mode 100644 index 0000000..d9a9d4e --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_stacked.scss @@ -0,0 +1,32 @@ +// stacking icons +// ------------------------- + +.#{$fa-css-prefix}-stack { + display: inline-block; + height: 2em; + line-height: 2em; + position: relative; + vertical-align: $fa-stack-vertical-align; + width: $fa-stack-width; +} + +.#{$fa-css-prefix}-stack-1x, +.#{$fa-css-prefix}-stack-2x { + left: 0; + position: absolute; + text-align: center; + width: 100%; + z-index: var(--#{$fa-css-prefix}-stack-z-index, #{$fa-stack-z-index}); +} + +.#{$fa-css-prefix}-stack-1x { + line-height: inherit; +} + +.#{$fa-css-prefix}-stack-2x { + font-size: 2em; +} + +.#{$fa-css-prefix}-inverse { + color: var(--#{$fa-css-prefix}-inverse, #{$fa-inverse}); +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_variables.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_variables.scss new file mode 100644 index 0000000..858541f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/_variables.scss @@ -0,0 +1,4961 @@ +// variables +// -------------------------- + +$fa-css-prefix : fa !default; +$fa-style : 900 !default; +$fa-style-family : "Font Awesome 6 Free" !default; + +$fa-display : inline-block !default; + +$fa-fw-width : fa-divide(20em, 16) !default; +$fa-inverse : #fff !default; + +$fa-border-color : #eee !default; +$fa-border-padding : .2em .25em .15em !default; +$fa-border-radius : .1em !default; +$fa-border-style : solid !default; +$fa-border-width : .08em !default; + +$fa-size-scale-2xs : 10 !default; +$fa-size-scale-xs : 12 !default; +$fa-size-scale-sm : 14 !default; +$fa-size-scale-base : 16 !default; +$fa-size-scale-lg : 20 !default; +$fa-size-scale-xl : 24 !default; +$fa-size-scale-2xl : 32 !default; + +$fa-sizes: ( + "2xs" : $fa-size-scale-2xs, + "xs" : $fa-size-scale-xs, + "sm" : $fa-size-scale-sm, + "lg" : $fa-size-scale-lg, + "xl" : $fa-size-scale-xl, + "2xl" : $fa-size-scale-2xl +) !default; + +$fa-li-width : 2em !default; +$fa-li-margin : $fa-li-width * fa-divide(5, 4) !default; + +$fa-pull-margin : .3em !default; + +$fa-primary-opacity : 1 !default; +$fa-secondary-opacity : .4 !default; + +$fa-stack-vertical-align: middle !default; +$fa-stack-width : ($fa-fw-width * 2) !default; +$fa-stack-z-index : auto !default; + +$fa-font-display : block !default; +$fa-font-path : "/fonts/fontawesome-free" !default; + +$fa-var-0: \30; +$fa-var-1: \31; +$fa-var-2: \32; +$fa-var-3: \33; +$fa-var-4: \34; +$fa-var-5: \35; +$fa-var-6: \36; +$fa-var-7: \37; +$fa-var-8: \38; +$fa-var-9: \39; +$fa-var-fill-drip: \f576; +$fa-var-arrows-to-circle: \e4bd; +$fa-var-circle-chevron-right: \f138; +$fa-var-chevron-circle-right: \f138; +$fa-var-at: \40; +$fa-var-trash-can: \f2ed; +$fa-var-trash-alt: \f2ed; +$fa-var-text-height: \f034; +$fa-var-user-xmark: \f235; +$fa-var-user-times: \f235; +$fa-var-stethoscope: \f0f1; +$fa-var-message: \f27a; +$fa-var-comment-alt: \f27a; +$fa-var-info: \f129; +$fa-var-down-left-and-up-right-to-center: \f422; +$fa-var-compress-alt: \f422; +$fa-var-explosion: \e4e9; +$fa-var-file-lines: \f15c; +$fa-var-file-alt: \f15c; +$fa-var-file-text: \f15c; +$fa-var-wave-square: \f83e; +$fa-var-ring: \f70b; +$fa-var-building-un: \e4d9; +$fa-var-dice-three: \f527; +$fa-var-calendar-days: \f073; +$fa-var-calendar-alt: \f073; +$fa-var-anchor-circle-check: \e4aa; +$fa-var-building-circle-arrow-right: \e4d1; +$fa-var-volleyball: \f45f; +$fa-var-volleyball-ball: \f45f; +$fa-var-arrows-up-to-line: \e4c2; +$fa-var-sort-down: \f0dd; +$fa-var-sort-desc: \f0dd; +$fa-var-circle-minus: \f056; +$fa-var-minus-circle: \f056; +$fa-var-door-open: \f52b; +$fa-var-right-from-bracket: \f2f5; +$fa-var-sign-out-alt: \f2f5; +$fa-var-atom: \f5d2; +$fa-var-soap: \e06e; +$fa-var-icons: \f86d; +$fa-var-heart-music-camera-bolt: \f86d; +$fa-var-microphone-lines-slash: \f539; +$fa-var-microphone-alt-slash: \f539; +$fa-var-bridge-circle-check: \e4c9; +$fa-var-pump-medical: \e06a; +$fa-var-fingerprint: \f577; +$fa-var-hand-point-right: \f0a4; +$fa-var-magnifying-glass-location: \f689; +$fa-var-search-location: \f689; +$fa-var-forward-step: \f051; +$fa-var-step-forward: \f051; +$fa-var-face-smile-beam: \f5b8; +$fa-var-smile-beam: \f5b8; +$fa-var-flag-checkered: \f11e; +$fa-var-football: \f44e; +$fa-var-football-ball: \f44e; +$fa-var-school-circle-exclamation: \e56c; +$fa-var-crop: \f125; +$fa-var-angles-down: \f103; +$fa-var-angle-double-down: \f103; +$fa-var-users-rectangle: \e594; +$fa-var-people-roof: \e537; +$fa-var-people-line: \e534; +$fa-var-beer-mug-empty: \f0fc; +$fa-var-beer: \f0fc; +$fa-var-diagram-predecessor: \e477; +$fa-var-arrow-up-long: \f176; +$fa-var-long-arrow-up: \f176; +$fa-var-fire-flame-simple: \f46a; +$fa-var-burn: \f46a; +$fa-var-person: \f183; +$fa-var-male: \f183; +$fa-var-laptop: \f109; +$fa-var-file-csv: \f6dd; +$fa-var-menorah: \f676; +$fa-var-truck-plane: \e58f; +$fa-var-record-vinyl: \f8d9; +$fa-var-face-grin-stars: \f587; +$fa-var-grin-stars: \f587; +$fa-var-bong: \f55c; +$fa-var-spaghetti-monster-flying: \f67b; +$fa-var-pastafarianism: \f67b; +$fa-var-arrow-down-up-across-line: \e4af; +$fa-var-spoon: \f2e5; +$fa-var-utensil-spoon: \f2e5; +$fa-var-jar-wheat: \e517; +$fa-var-envelopes-bulk: \f674; +$fa-var-mail-bulk: \f674; +$fa-var-file-circle-exclamation: \e4eb; +$fa-var-circle-h: \f47e; +$fa-var-hospital-symbol: \f47e; +$fa-var-pager: \f815; +$fa-var-address-book: \f2b9; +$fa-var-contact-book: \f2b9; +$fa-var-strikethrough: \f0cc; +$fa-var-k: \4b; +$fa-var-landmark-flag: \e51c; +$fa-var-pencil: \f303; +$fa-var-pencil-alt: \f303; +$fa-var-backward: \f04a; +$fa-var-caret-right: \f0da; +$fa-var-comments: \f086; +$fa-var-paste: \f0ea; +$fa-var-file-clipboard: \f0ea; +$fa-var-code-pull-request: \e13c; +$fa-var-clipboard-list: \f46d; +$fa-var-truck-ramp-box: \f4de; +$fa-var-truck-loading: \f4de; +$fa-var-user-check: \f4fc; +$fa-var-vial-virus: \e597; +$fa-var-sheet-plastic: \e571; +$fa-var-blog: \f781; +$fa-var-user-ninja: \f504; +$fa-var-person-arrow-up-from-line: \e539; +$fa-var-scroll-torah: \f6a0; +$fa-var-torah: \f6a0; +$fa-var-broom-ball: \f458; +$fa-var-quidditch: \f458; +$fa-var-quidditch-broom-ball: \f458; +$fa-var-toggle-off: \f204; +$fa-var-box-archive: \f187; +$fa-var-archive: \f187; +$fa-var-person-drowning: \e545; +$fa-var-arrow-down-9-1: \f886; +$fa-var-sort-numeric-desc: \f886; +$fa-var-sort-numeric-down-alt: \f886; +$fa-var-face-grin-tongue-squint: \f58a; +$fa-var-grin-tongue-squint: \f58a; +$fa-var-spray-can: \f5bd; +$fa-var-truck-monster: \f63b; +$fa-var-w: \57; +$fa-var-earth-africa: \f57c; +$fa-var-globe-africa: \f57c; +$fa-var-rainbow: \f75b; +$fa-var-circle-notch: \f1ce; +$fa-var-tablet-screen-button: \f3fa; +$fa-var-tablet-alt: \f3fa; +$fa-var-paw: \f1b0; +$fa-var-cloud: \f0c2; +$fa-var-trowel-bricks: \e58a; +$fa-var-face-flushed: \f579; +$fa-var-flushed: \f579; +$fa-var-hospital-user: \f80d; +$fa-var-tent-arrow-left-right: \e57f; +$fa-var-gavel: \f0e3; +$fa-var-legal: \f0e3; +$fa-var-binoculars: \f1e5; +$fa-var-microphone-slash: \f131; +$fa-var-box-tissue: \e05b; +$fa-var-motorcycle: \f21c; +$fa-var-bell-concierge: \f562; +$fa-var-concierge-bell: \f562; +$fa-var-pen-ruler: \f5ae; +$fa-var-pencil-ruler: \f5ae; +$fa-var-people-arrows: \e068; +$fa-var-people-arrows-left-right: \e068; +$fa-var-mars-and-venus-burst: \e523; +$fa-var-square-caret-right: \f152; +$fa-var-caret-square-right: \f152; +$fa-var-scissors: \f0c4; +$fa-var-cut: \f0c4; +$fa-var-sun-plant-wilt: \e57a; +$fa-var-toilets-portable: \e584; +$fa-var-hockey-puck: \f453; +$fa-var-table: \f0ce; +$fa-var-magnifying-glass-arrow-right: \e521; +$fa-var-tachograph-digital: \f566; +$fa-var-digital-tachograph: \f566; +$fa-var-users-slash: \e073; +$fa-var-clover: \e139; +$fa-var-reply: \f3e5; +$fa-var-mail-reply: \f3e5; +$fa-var-star-and-crescent: \f699; +$fa-var-house-fire: \e50c; +$fa-var-square-minus: \f146; +$fa-var-minus-square: \f146; +$fa-var-helicopter: \f533; +$fa-var-compass: \f14e; +$fa-var-square-caret-down: \f150; +$fa-var-caret-square-down: \f150; +$fa-var-file-circle-question: \e4ef; +$fa-var-laptop-code: \f5fc; +$fa-var-swatchbook: \f5c3; +$fa-var-prescription-bottle: \f485; +$fa-var-bars: \f0c9; +$fa-var-navicon: \f0c9; +$fa-var-people-group: \e533; +$fa-var-hourglass-end: \f253; +$fa-var-hourglass-3: \f253; +$fa-var-heart-crack: \f7a9; +$fa-var-heart-broken: \f7a9; +$fa-var-square-up-right: \f360; +$fa-var-external-link-square-alt: \f360; +$fa-var-face-kiss-beam: \f597; +$fa-var-kiss-beam: \f597; +$fa-var-film: \f008; +$fa-var-ruler-horizontal: \f547; +$fa-var-people-robbery: \e536; +$fa-var-lightbulb: \f0eb; +$fa-var-caret-left: \f0d9; +$fa-var-circle-exclamation: \f06a; +$fa-var-exclamation-circle: \f06a; +$fa-var-school-circle-xmark: \e56d; +$fa-var-arrow-right-from-bracket: \f08b; +$fa-var-sign-out: \f08b; +$fa-var-circle-chevron-down: \f13a; +$fa-var-chevron-circle-down: \f13a; +$fa-var-unlock-keyhole: \f13e; +$fa-var-unlock-alt: \f13e; +$fa-var-cloud-showers-heavy: \f740; +$fa-var-headphones-simple: \f58f; +$fa-var-headphones-alt: \f58f; +$fa-var-sitemap: \f0e8; +$fa-var-circle-dollar-to-slot: \f4b9; +$fa-var-donate: \f4b9; +$fa-var-memory: \f538; +$fa-var-road-spikes: \e568; +$fa-var-fire-burner: \e4f1; +$fa-var-flag: \f024; +$fa-var-hanukiah: \f6e6; +$fa-var-feather: \f52d; +$fa-var-volume-low: \f027; +$fa-var-volume-down: \f027; +$fa-var-comment-slash: \f4b3; +$fa-var-cloud-sun-rain: \f743; +$fa-var-compress: \f066; +$fa-var-wheat-awn: \e2cd; +$fa-var-wheat-alt: \e2cd; +$fa-var-ankh: \f644; +$fa-var-hands-holding-child: \e4fa; +$fa-var-asterisk: \2a; +$fa-var-square-check: \f14a; +$fa-var-check-square: \f14a; +$fa-var-peseta-sign: \e221; +$fa-var-heading: \f1dc; +$fa-var-header: \f1dc; +$fa-var-ghost: \f6e2; +$fa-var-list: \f03a; +$fa-var-list-squares: \f03a; +$fa-var-square-phone-flip: \f87b; +$fa-var-phone-square-alt: \f87b; +$fa-var-cart-plus: \f217; +$fa-var-gamepad: \f11b; +$fa-var-circle-dot: \f192; +$fa-var-dot-circle: \f192; +$fa-var-face-dizzy: \f567; +$fa-var-dizzy: \f567; +$fa-var-egg: \f7fb; +$fa-var-house-medical-circle-xmark: \e513; +$fa-var-campground: \f6bb; +$fa-var-folder-plus: \f65e; +$fa-var-futbol: \f1e3; +$fa-var-futbol-ball: \f1e3; +$fa-var-soccer-ball: \f1e3; +$fa-var-paintbrush: \f1fc; +$fa-var-paint-brush: \f1fc; +$fa-var-lock: \f023; +$fa-var-gas-pump: \f52f; +$fa-var-hot-tub-person: \f593; +$fa-var-hot-tub: \f593; +$fa-var-map-location: \f59f; +$fa-var-map-marked: \f59f; +$fa-var-house-flood-water: \e50e; +$fa-var-tree: \f1bb; +$fa-var-bridge-lock: \e4cc; +$fa-var-sack-dollar: \f81d; +$fa-var-pen-to-square: \f044; +$fa-var-edit: \f044; +$fa-var-car-side: \f5e4; +$fa-var-share-nodes: \f1e0; +$fa-var-share-alt: \f1e0; +$fa-var-heart-circle-minus: \e4ff; +$fa-var-hourglass-half: \f252; +$fa-var-hourglass-2: \f252; +$fa-var-microscope: \f610; +$fa-var-sink: \e06d; +$fa-var-bag-shopping: \f290; +$fa-var-shopping-bag: \f290; +$fa-var-arrow-down-z-a: \f881; +$fa-var-sort-alpha-desc: \f881; +$fa-var-sort-alpha-down-alt: \f881; +$fa-var-mitten: \f7b5; +$fa-var-person-rays: \e54d; +$fa-var-users: \f0c0; +$fa-var-eye-slash: \f070; +$fa-var-flask-vial: \e4f3; +$fa-var-hand: \f256; +$fa-var-hand-paper: \f256; +$fa-var-om: \f679; +$fa-var-worm: \e599; +$fa-var-house-circle-xmark: \e50b; +$fa-var-plug: \f1e6; +$fa-var-chevron-up: \f077; +$fa-var-hand-spock: \f259; +$fa-var-stopwatch: \f2f2; +$fa-var-face-kiss: \f596; +$fa-var-kiss: \f596; +$fa-var-bridge-circle-xmark: \e4cb; +$fa-var-face-grin-tongue: \f589; +$fa-var-grin-tongue: \f589; +$fa-var-chess-bishop: \f43a; +$fa-var-face-grin-wink: \f58c; +$fa-var-grin-wink: \f58c; +$fa-var-ear-deaf: \f2a4; +$fa-var-deaf: \f2a4; +$fa-var-deafness: \f2a4; +$fa-var-hard-of-hearing: \f2a4; +$fa-var-road-circle-check: \e564; +$fa-var-dice-five: \f523; +$fa-var-square-rss: \f143; +$fa-var-rss-square: \f143; +$fa-var-land-mine-on: \e51b; +$fa-var-i-cursor: \f246; +$fa-var-stamp: \f5bf; +$fa-var-stairs: \e289; +$fa-var-i: \49; +$fa-var-hryvnia-sign: \f6f2; +$fa-var-hryvnia: \f6f2; +$fa-var-pills: \f484; +$fa-var-face-grin-wide: \f581; +$fa-var-grin-alt: \f581; +$fa-var-tooth: \f5c9; +$fa-var-v: \56; +$fa-var-bangladeshi-taka-sign: \e2e6; +$fa-var-bicycle: \f206; +$fa-var-staff-snake: \e579; +$fa-var-rod-asclepius: \e579; +$fa-var-rod-snake: \e579; +$fa-var-staff-aesculapius: \e579; +$fa-var-head-side-cough-slash: \e062; +$fa-var-truck-medical: \f0f9; +$fa-var-ambulance: \f0f9; +$fa-var-wheat-awn-circle-exclamation: \e598; +$fa-var-snowman: \f7d0; +$fa-var-mortar-pestle: \f5a7; +$fa-var-road-barrier: \e562; +$fa-var-school: \f549; +$fa-var-igloo: \f7ae; +$fa-var-joint: \f595; +$fa-var-angle-right: \f105; +$fa-var-horse: \f6f0; +$fa-var-q: \51; +$fa-var-g: \47; +$fa-var-notes-medical: \f481; +$fa-var-temperature-half: \f2c9; +$fa-var-temperature-2: \f2c9; +$fa-var-thermometer-2: \f2c9; +$fa-var-thermometer-half: \f2c9; +$fa-var-dong-sign: \e169; +$fa-var-capsules: \f46b; +$fa-var-poo-storm: \f75a; +$fa-var-poo-bolt: \f75a; +$fa-var-face-frown-open: \f57a; +$fa-var-frown-open: \f57a; +$fa-var-hand-point-up: \f0a6; +$fa-var-money-bill: \f0d6; +$fa-var-bookmark: \f02e; +$fa-var-align-justify: \f039; +$fa-var-umbrella-beach: \f5ca; +$fa-var-helmet-un: \e503; +$fa-var-bullseye: \f140; +$fa-var-bacon: \f7e5; +$fa-var-hand-point-down: \f0a7; +$fa-var-arrow-up-from-bracket: \e09a; +$fa-var-folder: \f07b; +$fa-var-folder-blank: \f07b; +$fa-var-file-waveform: \f478; +$fa-var-file-medical-alt: \f478; +$fa-var-radiation: \f7b9; +$fa-var-chart-simple: \e473; +$fa-var-mars-stroke: \f229; +$fa-var-vial: \f492; +$fa-var-gauge: \f624; +$fa-var-dashboard: \f624; +$fa-var-gauge-med: \f624; +$fa-var-tachometer-alt-average: \f624; +$fa-var-wand-magic-sparkles: \e2ca; +$fa-var-magic-wand-sparkles: \e2ca; +$fa-var-e: \45; +$fa-var-pen-clip: \f305; +$fa-var-pen-alt: \f305; +$fa-var-bridge-circle-exclamation: \e4ca; +$fa-var-user: \f007; +$fa-var-school-circle-check: \e56b; +$fa-var-dumpster: \f793; +$fa-var-van-shuttle: \f5b6; +$fa-var-shuttle-van: \f5b6; +$fa-var-building-user: \e4da; +$fa-var-square-caret-left: \f191; +$fa-var-caret-square-left: \f191; +$fa-var-highlighter: \f591; +$fa-var-key: \f084; +$fa-var-bullhorn: \f0a1; +$fa-var-globe: \f0ac; +$fa-var-synagogue: \f69b; +$fa-var-person-half-dress: \e548; +$fa-var-road-bridge: \e563; +$fa-var-location-arrow: \f124; +$fa-var-c: \43; +$fa-var-tablet-button: \f10a; +$fa-var-building-lock: \e4d6; +$fa-var-pizza-slice: \f818; +$fa-var-money-bill-wave: \f53a; +$fa-var-chart-area: \f1fe; +$fa-var-area-chart: \f1fe; +$fa-var-house-flag: \e50d; +$fa-var-person-circle-minus: \e540; +$fa-var-ban: \f05e; +$fa-var-cancel: \f05e; +$fa-var-camera-rotate: \e0d8; +$fa-var-spray-can-sparkles: \f5d0; +$fa-var-air-freshener: \f5d0; +$fa-var-star: \f005; +$fa-var-repeat: \f363; +$fa-var-cross: \f654; +$fa-var-box: \f466; +$fa-var-venus-mars: \f228; +$fa-var-arrow-pointer: \f245; +$fa-var-mouse-pointer: \f245; +$fa-var-maximize: \f31e; +$fa-var-expand-arrows-alt: \f31e; +$fa-var-charging-station: \f5e7; +$fa-var-shapes: \f61f; +$fa-var-triangle-circle-square: \f61f; +$fa-var-shuffle: \f074; +$fa-var-random: \f074; +$fa-var-person-running: \f70c; +$fa-var-running: \f70c; +$fa-var-mobile-retro: \e527; +$fa-var-grip-lines-vertical: \f7a5; +$fa-var-spider: \f717; +$fa-var-hands-bound: \e4f9; +$fa-var-file-invoice-dollar: \f571; +$fa-var-plane-circle-exclamation: \e556; +$fa-var-x-ray: \f497; +$fa-var-spell-check: \f891; +$fa-var-slash: \f715; +$fa-var-computer-mouse: \f8cc; +$fa-var-mouse: \f8cc; +$fa-var-arrow-right-to-bracket: \f090; +$fa-var-sign-in: \f090; +$fa-var-shop-slash: \e070; +$fa-var-store-alt-slash: \e070; +$fa-var-server: \f233; +$fa-var-virus-covid-slash: \e4a9; +$fa-var-shop-lock: \e4a5; +$fa-var-hourglass-start: \f251; +$fa-var-hourglass-1: \f251; +$fa-var-blender-phone: \f6b6; +$fa-var-building-wheat: \e4db; +$fa-var-person-breastfeeding: \e53a; +$fa-var-right-to-bracket: \f2f6; +$fa-var-sign-in-alt: \f2f6; +$fa-var-venus: \f221; +$fa-var-passport: \f5ab; +$fa-var-heart-pulse: \f21e; +$fa-var-heartbeat: \f21e; +$fa-var-people-carry-box: \f4ce; +$fa-var-people-carry: \f4ce; +$fa-var-temperature-high: \f769; +$fa-var-microchip: \f2db; +$fa-var-crown: \f521; +$fa-var-weight-hanging: \f5cd; +$fa-var-xmarks-lines: \e59a; +$fa-var-file-prescription: \f572; +$fa-var-weight-scale: \f496; +$fa-var-weight: \f496; +$fa-var-user-group: \f500; +$fa-var-user-friends: \f500; +$fa-var-arrow-up-a-z: \f15e; +$fa-var-sort-alpha-up: \f15e; +$fa-var-chess-knight: \f441; +$fa-var-face-laugh-squint: \f59b; +$fa-var-laugh-squint: \f59b; +$fa-var-wheelchair: \f193; +$fa-var-circle-arrow-up: \f0aa; +$fa-var-arrow-circle-up: \f0aa; +$fa-var-toggle-on: \f205; +$fa-var-person-walking: \f554; +$fa-var-walking: \f554; +$fa-var-l: \4c; +$fa-var-fire: \f06d; +$fa-var-bed-pulse: \f487; +$fa-var-procedures: \f487; +$fa-var-shuttle-space: \f197; +$fa-var-space-shuttle: \f197; +$fa-var-face-laugh: \f599; +$fa-var-laugh: \f599; +$fa-var-folder-open: \f07c; +$fa-var-heart-circle-plus: \e500; +$fa-var-code-fork: \e13b; +$fa-var-city: \f64f; +$fa-var-microphone-lines: \f3c9; +$fa-var-microphone-alt: \f3c9; +$fa-var-pepper-hot: \f816; +$fa-var-unlock: \f09c; +$fa-var-colon-sign: \e140; +$fa-var-headset: \f590; +$fa-var-store-slash: \e071; +$fa-var-road-circle-xmark: \e566; +$fa-var-user-minus: \f503; +$fa-var-mars-stroke-up: \f22a; +$fa-var-mars-stroke-v: \f22a; +$fa-var-champagne-glasses: \f79f; +$fa-var-glass-cheers: \f79f; +$fa-var-clipboard: \f328; +$fa-var-house-circle-exclamation: \e50a; +$fa-var-file-arrow-up: \f574; +$fa-var-file-upload: \f574; +$fa-var-wifi: \f1eb; +$fa-var-wifi-3: \f1eb; +$fa-var-wifi-strong: \f1eb; +$fa-var-bath: \f2cd; +$fa-var-bathtub: \f2cd; +$fa-var-underline: \f0cd; +$fa-var-user-pen: \f4ff; +$fa-var-user-edit: \f4ff; +$fa-var-signature: \f5b7; +$fa-var-stroopwafel: \f551; +$fa-var-bold: \f032; +$fa-var-anchor-lock: \e4ad; +$fa-var-building-ngo: \e4d7; +$fa-var-manat-sign: \e1d5; +$fa-var-not-equal: \f53e; +$fa-var-border-top-left: \f853; +$fa-var-border-style: \f853; +$fa-var-map-location-dot: \f5a0; +$fa-var-map-marked-alt: \f5a0; +$fa-var-jedi: \f669; +$fa-var-square-poll-vertical: \f681; +$fa-var-poll: \f681; +$fa-var-mug-hot: \f7b6; +$fa-var-car-battery: \f5df; +$fa-var-battery-car: \f5df; +$fa-var-gift: \f06b; +$fa-var-dice-two: \f528; +$fa-var-chess-queen: \f445; +$fa-var-glasses: \f530; +$fa-var-chess-board: \f43c; +$fa-var-building-circle-check: \e4d2; +$fa-var-person-chalkboard: \e53d; +$fa-var-mars-stroke-right: \f22b; +$fa-var-mars-stroke-h: \f22b; +$fa-var-hand-back-fist: \f255; +$fa-var-hand-rock: \f255; +$fa-var-square-caret-up: \f151; +$fa-var-caret-square-up: \f151; +$fa-var-cloud-showers-water: \e4e4; +$fa-var-chart-bar: \f080; +$fa-var-bar-chart: \f080; +$fa-var-hands-bubbles: \e05e; +$fa-var-hands-wash: \e05e; +$fa-var-less-than-equal: \f537; +$fa-var-train: \f238; +$fa-var-eye-low-vision: \f2a8; +$fa-var-low-vision: \f2a8; +$fa-var-crow: \f520; +$fa-var-sailboat: \e445; +$fa-var-window-restore: \f2d2; +$fa-var-square-plus: \f0fe; +$fa-var-plus-square: \f0fe; +$fa-var-torii-gate: \f6a1; +$fa-var-frog: \f52e; +$fa-var-bucket: \e4cf; +$fa-var-image: \f03e; +$fa-var-microphone: \f130; +$fa-var-cow: \f6c8; +$fa-var-caret-up: \f0d8; +$fa-var-screwdriver: \f54a; +$fa-var-folder-closed: \e185; +$fa-var-house-tsunami: \e515; +$fa-var-square-nfi: \e576; +$fa-var-arrow-up-from-ground-water: \e4b5; +$fa-var-martini-glass: \f57b; +$fa-var-glass-martini-alt: \f57b; +$fa-var-rotate-left: \f2ea; +$fa-var-rotate-back: \f2ea; +$fa-var-rotate-backward: \f2ea; +$fa-var-undo-alt: \f2ea; +$fa-var-table-columns: \f0db; +$fa-var-columns: \f0db; +$fa-var-lemon: \f094; +$fa-var-head-side-mask: \e063; +$fa-var-handshake: \f2b5; +$fa-var-gem: \f3a5; +$fa-var-dolly: \f472; +$fa-var-dolly-box: \f472; +$fa-var-smoking: \f48d; +$fa-var-minimize: \f78c; +$fa-var-compress-arrows-alt: \f78c; +$fa-var-monument: \f5a6; +$fa-var-snowplow: \f7d2; +$fa-var-angles-right: \f101; +$fa-var-angle-double-right: \f101; +$fa-var-cannabis: \f55f; +$fa-var-circle-play: \f144; +$fa-var-play-circle: \f144; +$fa-var-tablets: \f490; +$fa-var-ethernet: \f796; +$fa-var-euro-sign: \f153; +$fa-var-eur: \f153; +$fa-var-euro: \f153; +$fa-var-chair: \f6c0; +$fa-var-circle-check: \f058; +$fa-var-check-circle: \f058; +$fa-var-circle-stop: \f28d; +$fa-var-stop-circle: \f28d; +$fa-var-compass-drafting: \f568; +$fa-var-drafting-compass: \f568; +$fa-var-plate-wheat: \e55a; +$fa-var-icicles: \f7ad; +$fa-var-person-shelter: \e54f; +$fa-var-neuter: \f22c; +$fa-var-id-badge: \f2c1; +$fa-var-marker: \f5a1; +$fa-var-face-laugh-beam: \f59a; +$fa-var-laugh-beam: \f59a; +$fa-var-helicopter-symbol: \e502; +$fa-var-universal-access: \f29a; +$fa-var-circle-chevron-up: \f139; +$fa-var-chevron-circle-up: \f139; +$fa-var-lari-sign: \e1c8; +$fa-var-volcano: \f770; +$fa-var-person-walking-dashed-line-arrow-right: \e553; +$fa-var-sterling-sign: \f154; +$fa-var-gbp: \f154; +$fa-var-pound-sign: \f154; +$fa-var-viruses: \e076; +$fa-var-square-person-confined: \e577; +$fa-var-user-tie: \f508; +$fa-var-arrow-down-long: \f175; +$fa-var-long-arrow-down: \f175; +$fa-var-tent-arrow-down-to-line: \e57e; +$fa-var-certificate: \f0a3; +$fa-var-reply-all: \f122; +$fa-var-mail-reply-all: \f122; +$fa-var-suitcase: \f0f2; +$fa-var-person-skating: \f7c5; +$fa-var-skating: \f7c5; +$fa-var-filter-circle-dollar: \f662; +$fa-var-funnel-dollar: \f662; +$fa-var-camera-retro: \f083; +$fa-var-circle-arrow-down: \f0ab; +$fa-var-arrow-circle-down: \f0ab; +$fa-var-file-import: \f56f; +$fa-var-arrow-right-to-file: \f56f; +$fa-var-square-arrow-up-right: \f14c; +$fa-var-external-link-square: \f14c; +$fa-var-box-open: \f49e; +$fa-var-scroll: \f70e; +$fa-var-spa: \f5bb; +$fa-var-location-pin-lock: \e51f; +$fa-var-pause: \f04c; +$fa-var-hill-avalanche: \e507; +$fa-var-temperature-empty: \f2cb; +$fa-var-temperature-0: \f2cb; +$fa-var-thermometer-0: \f2cb; +$fa-var-thermometer-empty: \f2cb; +$fa-var-bomb: \f1e2; +$fa-var-registered: \f25d; +$fa-var-address-card: \f2bb; +$fa-var-contact-card: \f2bb; +$fa-var-vcard: \f2bb; +$fa-var-scale-unbalanced-flip: \f516; +$fa-var-balance-scale-right: \f516; +$fa-var-subscript: \f12c; +$fa-var-diamond-turn-right: \f5eb; +$fa-var-directions: \f5eb; +$fa-var-burst: \e4dc; +$fa-var-house-laptop: \e066; +$fa-var-laptop-house: \e066; +$fa-var-face-tired: \f5c8; +$fa-var-tired: \f5c8; +$fa-var-money-bills: \e1f3; +$fa-var-smog: \f75f; +$fa-var-crutch: \f7f7; +$fa-var-cloud-arrow-up: \f0ee; +$fa-var-cloud-upload: \f0ee; +$fa-var-cloud-upload-alt: \f0ee; +$fa-var-palette: \f53f; +$fa-var-arrows-turn-right: \e4c0; +$fa-var-vest: \e085; +$fa-var-ferry: \e4ea; +$fa-var-arrows-down-to-people: \e4b9; +$fa-var-seedling: \f4d8; +$fa-var-sprout: \f4d8; +$fa-var-left-right: \f337; +$fa-var-arrows-alt-h: \f337; +$fa-var-boxes-packing: \e4c7; +$fa-var-circle-arrow-left: \f0a8; +$fa-var-arrow-circle-left: \f0a8; +$fa-var-group-arrows-rotate: \e4f6; +$fa-var-bowl-food: \e4c6; +$fa-var-candy-cane: \f786; +$fa-var-arrow-down-wide-short: \f160; +$fa-var-sort-amount-asc: \f160; +$fa-var-sort-amount-down: \f160; +$fa-var-cloud-bolt: \f76c; +$fa-var-thunderstorm: \f76c; +$fa-var-text-slash: \f87d; +$fa-var-remove-format: \f87d; +$fa-var-face-smile-wink: \f4da; +$fa-var-smile-wink: \f4da; +$fa-var-file-word: \f1c2; +$fa-var-file-powerpoint: \f1c4; +$fa-var-arrows-left-right: \f07e; +$fa-var-arrows-h: \f07e; +$fa-var-house-lock: \e510; +$fa-var-cloud-arrow-down: \f0ed; +$fa-var-cloud-download: \f0ed; +$fa-var-cloud-download-alt: \f0ed; +$fa-var-children: \e4e1; +$fa-var-chalkboard: \f51b; +$fa-var-blackboard: \f51b; +$fa-var-user-large-slash: \f4fa; +$fa-var-user-alt-slash: \f4fa; +$fa-var-envelope-open: \f2b6; +$fa-var-handshake-simple-slash: \e05f; +$fa-var-handshake-alt-slash: \e05f; +$fa-var-mattress-pillow: \e525; +$fa-var-guarani-sign: \e19a; +$fa-var-arrows-rotate: \f021; +$fa-var-refresh: \f021; +$fa-var-sync: \f021; +$fa-var-fire-extinguisher: \f134; +$fa-var-cruzeiro-sign: \e152; +$fa-var-greater-than-equal: \f532; +$fa-var-shield-halved: \f3ed; +$fa-var-shield-alt: \f3ed; +$fa-var-book-atlas: \f558; +$fa-var-atlas: \f558; +$fa-var-virus: \e074; +$fa-var-envelope-circle-check: \e4e8; +$fa-var-layer-group: \f5fd; +$fa-var-arrows-to-dot: \e4be; +$fa-var-archway: \f557; +$fa-var-heart-circle-check: \e4fd; +$fa-var-house-chimney-crack: \f6f1; +$fa-var-house-damage: \f6f1; +$fa-var-file-zipper: \f1c6; +$fa-var-file-archive: \f1c6; +$fa-var-square: \f0c8; +$fa-var-martini-glass-empty: \f000; +$fa-var-glass-martini: \f000; +$fa-var-couch: \f4b8; +$fa-var-cedi-sign: \e0df; +$fa-var-italic: \f033; +$fa-var-church: \f51d; +$fa-var-comments-dollar: \f653; +$fa-var-democrat: \f747; +$fa-var-z: \5a; +$fa-var-person-skiing: \f7c9; +$fa-var-skiing: \f7c9; +$fa-var-road-lock: \e567; +$fa-var-a: \41; +$fa-var-temperature-arrow-down: \e03f; +$fa-var-temperature-down: \e03f; +$fa-var-feather-pointed: \f56b; +$fa-var-feather-alt: \f56b; +$fa-var-p: \50; +$fa-var-snowflake: \f2dc; +$fa-var-newspaper: \f1ea; +$fa-var-rectangle-ad: \f641; +$fa-var-ad: \f641; +$fa-var-circle-arrow-right: \f0a9; +$fa-var-arrow-circle-right: \f0a9; +$fa-var-filter-circle-xmark: \e17b; +$fa-var-locust: \e520; +$fa-var-sort: \f0dc; +$fa-var-unsorted: \f0dc; +$fa-var-list-ol: \f0cb; +$fa-var-list-1-2: \f0cb; +$fa-var-list-numeric: \f0cb; +$fa-var-person-dress-burst: \e544; +$fa-var-money-check-dollar: \f53d; +$fa-var-money-check-alt: \f53d; +$fa-var-vector-square: \f5cb; +$fa-var-bread-slice: \f7ec; +$fa-var-language: \f1ab; +$fa-var-face-kiss-wink-heart: \f598; +$fa-var-kiss-wink-heart: \f598; +$fa-var-filter: \f0b0; +$fa-var-question: \3f; +$fa-var-file-signature: \f573; +$fa-var-up-down-left-right: \f0b2; +$fa-var-arrows-alt: \f0b2; +$fa-var-house-chimney-user: \e065; +$fa-var-hand-holding-heart: \f4be; +$fa-var-puzzle-piece: \f12e; +$fa-var-money-check: \f53c; +$fa-var-star-half-stroke: \f5c0; +$fa-var-star-half-alt: \f5c0; +$fa-var-code: \f121; +$fa-var-whiskey-glass: \f7a0; +$fa-var-glass-whiskey: \f7a0; +$fa-var-building-circle-exclamation: \e4d3; +$fa-var-magnifying-glass-chart: \e522; +$fa-var-arrow-up-right-from-square: \f08e; +$fa-var-external-link: \f08e; +$fa-var-cubes-stacked: \e4e6; +$fa-var-won-sign: \f159; +$fa-var-krw: \f159; +$fa-var-won: \f159; +$fa-var-virus-covid: \e4a8; +$fa-var-austral-sign: \e0a9; +$fa-var-f: \46; +$fa-var-leaf: \f06c; +$fa-var-road: \f018; +$fa-var-taxi: \f1ba; +$fa-var-cab: \f1ba; +$fa-var-person-circle-plus: \e541; +$fa-var-chart-pie: \f200; +$fa-var-pie-chart: \f200; +$fa-var-bolt-lightning: \e0b7; +$fa-var-sack-xmark: \e56a; +$fa-var-file-excel: \f1c3; +$fa-var-file-contract: \f56c; +$fa-var-fish-fins: \e4f2; +$fa-var-building-flag: \e4d5; +$fa-var-face-grin-beam: \f582; +$fa-var-grin-beam: \f582; +$fa-var-object-ungroup: \f248; +$fa-var-poop: \f619; +$fa-var-location-pin: \f041; +$fa-var-map-marker: \f041; +$fa-var-kaaba: \f66b; +$fa-var-toilet-paper: \f71e; +$fa-var-helmet-safety: \f807; +$fa-var-hard-hat: \f807; +$fa-var-hat-hard: \f807; +$fa-var-eject: \f052; +$fa-var-circle-right: \f35a; +$fa-var-arrow-alt-circle-right: \f35a; +$fa-var-plane-circle-check: \e555; +$fa-var-face-rolling-eyes: \f5a5; +$fa-var-meh-rolling-eyes: \f5a5; +$fa-var-object-group: \f247; +$fa-var-chart-line: \f201; +$fa-var-line-chart: \f201; +$fa-var-mask-ventilator: \e524; +$fa-var-arrow-right: \f061; +$fa-var-signs-post: \f277; +$fa-var-map-signs: \f277; +$fa-var-cash-register: \f788; +$fa-var-person-circle-question: \e542; +$fa-var-h: \48; +$fa-var-tarp: \e57b; +$fa-var-screwdriver-wrench: \f7d9; +$fa-var-tools: \f7d9; +$fa-var-arrows-to-eye: \e4bf; +$fa-var-plug-circle-bolt: \e55b; +$fa-var-heart: \f004; +$fa-var-mars-and-venus: \f224; +$fa-var-house-user: \e1b0; +$fa-var-home-user: \e1b0; +$fa-var-dumpster-fire: \f794; +$fa-var-house-crack: \e3b1; +$fa-var-martini-glass-citrus: \f561; +$fa-var-cocktail: \f561; +$fa-var-face-surprise: \f5c2; +$fa-var-surprise: \f5c2; +$fa-var-bottle-water: \e4c5; +$fa-var-circle-pause: \f28b; +$fa-var-pause-circle: \f28b; +$fa-var-toilet-paper-slash: \e072; +$fa-var-apple-whole: \f5d1; +$fa-var-apple-alt: \f5d1; +$fa-var-kitchen-set: \e51a; +$fa-var-r: \52; +$fa-var-temperature-quarter: \f2ca; +$fa-var-temperature-1: \f2ca; +$fa-var-thermometer-1: \f2ca; +$fa-var-thermometer-quarter: \f2ca; +$fa-var-cube: \f1b2; +$fa-var-bitcoin-sign: \e0b4; +$fa-var-shield-dog: \e573; +$fa-var-solar-panel: \f5ba; +$fa-var-lock-open: \f3c1; +$fa-var-elevator: \e16d; +$fa-var-money-bill-transfer: \e528; +$fa-var-money-bill-trend-up: \e529; +$fa-var-house-flood-water-circle-arrow-right: \e50f; +$fa-var-square-poll-horizontal: \f682; +$fa-var-poll-h: \f682; +$fa-var-circle: \f111; +$fa-var-backward-fast: \f049; +$fa-var-fast-backward: \f049; +$fa-var-recycle: \f1b8; +$fa-var-user-astronaut: \f4fb; +$fa-var-plane-slash: \e069; +$fa-var-trademark: \f25c; +$fa-var-basketball: \f434; +$fa-var-basketball-ball: \f434; +$fa-var-satellite-dish: \f7c0; +$fa-var-circle-up: \f35b; +$fa-var-arrow-alt-circle-up: \f35b; +$fa-var-mobile-screen-button: \f3cd; +$fa-var-mobile-alt: \f3cd; +$fa-var-volume-high: \f028; +$fa-var-volume-up: \f028; +$fa-var-users-rays: \e593; +$fa-var-wallet: \f555; +$fa-var-clipboard-check: \f46c; +$fa-var-file-audio: \f1c7; +$fa-var-burger: \f805; +$fa-var-hamburger: \f805; +$fa-var-wrench: \f0ad; +$fa-var-bugs: \e4d0; +$fa-var-rupee-sign: \f156; +$fa-var-rupee: \f156; +$fa-var-file-image: \f1c5; +$fa-var-circle-question: \f059; +$fa-var-question-circle: \f059; +$fa-var-plane-departure: \f5b0; +$fa-var-handshake-slash: \e060; +$fa-var-book-bookmark: \e0bb; +$fa-var-code-branch: \f126; +$fa-var-hat-cowboy: \f8c0; +$fa-var-bridge: \e4c8; +$fa-var-phone-flip: \f879; +$fa-var-phone-alt: \f879; +$fa-var-truck-front: \e2b7; +$fa-var-cat: \f6be; +$fa-var-anchor-circle-exclamation: \e4ab; +$fa-var-truck-field: \e58d; +$fa-var-route: \f4d7; +$fa-var-clipboard-question: \e4e3; +$fa-var-panorama: \e209; +$fa-var-comment-medical: \f7f5; +$fa-var-teeth-open: \f62f; +$fa-var-file-circle-minus: \e4ed; +$fa-var-tags: \f02c; +$fa-var-wine-glass: \f4e3; +$fa-var-forward-fast: \f050; +$fa-var-fast-forward: \f050; +$fa-var-face-meh-blank: \f5a4; +$fa-var-meh-blank: \f5a4; +$fa-var-square-parking: \f540; +$fa-var-parking: \f540; +$fa-var-house-signal: \e012; +$fa-var-bars-progress: \f828; +$fa-var-tasks-alt: \f828; +$fa-var-faucet-drip: \e006; +$fa-var-cart-flatbed: \f474; +$fa-var-dolly-flatbed: \f474; +$fa-var-ban-smoking: \f54d; +$fa-var-smoking-ban: \f54d; +$fa-var-terminal: \f120; +$fa-var-mobile-button: \f10b; +$fa-var-house-medical-flag: \e514; +$fa-var-basket-shopping: \f291; +$fa-var-shopping-basket: \f291; +$fa-var-tape: \f4db; +$fa-var-bus-simple: \f55e; +$fa-var-bus-alt: \f55e; +$fa-var-eye: \f06e; +$fa-var-face-sad-cry: \f5b3; +$fa-var-sad-cry: \f5b3; +$fa-var-audio-description: \f29e; +$fa-var-person-military-to-person: \e54c; +$fa-var-file-shield: \e4f0; +$fa-var-user-slash: \f506; +$fa-var-pen: \f304; +$fa-var-tower-observation: \e586; +$fa-var-file-code: \f1c9; +$fa-var-signal: \f012; +$fa-var-signal-5: \f012; +$fa-var-signal-perfect: \f012; +$fa-var-bus: \f207; +$fa-var-heart-circle-xmark: \e501; +$fa-var-house-chimney: \e3af; +$fa-var-home-lg: \e3af; +$fa-var-window-maximize: \f2d0; +$fa-var-face-frown: \f119; +$fa-var-frown: \f119; +$fa-var-prescription: \f5b1; +$fa-var-shop: \f54f; +$fa-var-store-alt: \f54f; +$fa-var-floppy-disk: \f0c7; +$fa-var-save: \f0c7; +$fa-var-vihara: \f6a7; +$fa-var-scale-unbalanced: \f515; +$fa-var-balance-scale-left: \f515; +$fa-var-sort-up: \f0de; +$fa-var-sort-asc: \f0de; +$fa-var-comment-dots: \f4ad; +$fa-var-commenting: \f4ad; +$fa-var-plant-wilt: \e5aa; +$fa-var-diamond: \f219; +$fa-var-face-grin-squint: \f585; +$fa-var-grin-squint: \f585; +$fa-var-hand-holding-dollar: \f4c0; +$fa-var-hand-holding-usd: \f4c0; +$fa-var-bacterium: \e05a; +$fa-var-hand-pointer: \f25a; +$fa-var-drum-steelpan: \f56a; +$fa-var-hand-scissors: \f257; +$fa-var-hands-praying: \f684; +$fa-var-praying-hands: \f684; +$fa-var-arrow-rotate-right: \f01e; +$fa-var-arrow-right-rotate: \f01e; +$fa-var-arrow-rotate-forward: \f01e; +$fa-var-redo: \f01e; +$fa-var-biohazard: \f780; +$fa-var-location-crosshairs: \f601; +$fa-var-location: \f601; +$fa-var-mars-double: \f227; +$fa-var-child-dress: \e59c; +$fa-var-users-between-lines: \e591; +$fa-var-lungs-virus: \e067; +$fa-var-face-grin-tears: \f588; +$fa-var-grin-tears: \f588; +$fa-var-phone: \f095; +$fa-var-calendar-xmark: \f273; +$fa-var-calendar-times: \f273; +$fa-var-child-reaching: \e59d; +$fa-var-head-side-virus: \e064; +$fa-var-user-gear: \f4fe; +$fa-var-user-cog: \f4fe; +$fa-var-arrow-up-1-9: \f163; +$fa-var-sort-numeric-up: \f163; +$fa-var-door-closed: \f52a; +$fa-var-shield-virus: \e06c; +$fa-var-dice-six: \f526; +$fa-var-mosquito-net: \e52c; +$fa-var-bridge-water: \e4ce; +$fa-var-person-booth: \f756; +$fa-var-text-width: \f035; +$fa-var-hat-wizard: \f6e8; +$fa-var-pen-fancy: \f5ac; +$fa-var-person-digging: \f85e; +$fa-var-digging: \f85e; +$fa-var-trash: \f1f8; +$fa-var-gauge-simple: \f629; +$fa-var-gauge-simple-med: \f629; +$fa-var-tachometer-average: \f629; +$fa-var-book-medical: \f7e6; +$fa-var-poo: \f2fe; +$fa-var-quote-right: \f10e; +$fa-var-quote-right-alt: \f10e; +$fa-var-shirt: \f553; +$fa-var-t-shirt: \f553; +$fa-var-tshirt: \f553; +$fa-var-cubes: \f1b3; +$fa-var-divide: \f529; +$fa-var-tenge-sign: \f7d7; +$fa-var-tenge: \f7d7; +$fa-var-headphones: \f025; +$fa-var-hands-holding: \f4c2; +$fa-var-hands-clapping: \e1a8; +$fa-var-republican: \f75e; +$fa-var-arrow-left: \f060; +$fa-var-person-circle-xmark: \e543; +$fa-var-ruler: \f545; +$fa-var-align-left: \f036; +$fa-var-dice-d6: \f6d1; +$fa-var-restroom: \f7bd; +$fa-var-j: \4a; +$fa-var-users-viewfinder: \e595; +$fa-var-file-video: \f1c8; +$fa-var-up-right-from-square: \f35d; +$fa-var-external-link-alt: \f35d; +$fa-var-table-cells: \f00a; +$fa-var-th: \f00a; +$fa-var-file-pdf: \f1c1; +$fa-var-book-bible: \f647; +$fa-var-bible: \f647; +$fa-var-o: \4f; +$fa-var-suitcase-medical: \f0fa; +$fa-var-medkit: \f0fa; +$fa-var-user-secret: \f21b; +$fa-var-otter: \f700; +$fa-var-person-dress: \f182; +$fa-var-female: \f182; +$fa-var-comment-dollar: \f651; +$fa-var-business-time: \f64a; +$fa-var-briefcase-clock: \f64a; +$fa-var-table-cells-large: \f009; +$fa-var-th-large: \f009; +$fa-var-book-tanakh: \f827; +$fa-var-tanakh: \f827; +$fa-var-phone-volume: \f2a0; +$fa-var-volume-control-phone: \f2a0; +$fa-var-hat-cowboy-side: \f8c1; +$fa-var-clipboard-user: \f7f3; +$fa-var-child: \f1ae; +$fa-var-lira-sign: \f195; +$fa-var-satellite: \f7bf; +$fa-var-plane-lock: \e558; +$fa-var-tag: \f02b; +$fa-var-comment: \f075; +$fa-var-cake-candles: \f1fd; +$fa-var-birthday-cake: \f1fd; +$fa-var-cake: \f1fd; +$fa-var-envelope: \f0e0; +$fa-var-angles-up: \f102; +$fa-var-angle-double-up: \f102; +$fa-var-paperclip: \f0c6; +$fa-var-arrow-right-to-city: \e4b3; +$fa-var-ribbon: \f4d6; +$fa-var-lungs: \f604; +$fa-var-arrow-up-9-1: \f887; +$fa-var-sort-numeric-up-alt: \f887; +$fa-var-litecoin-sign: \e1d3; +$fa-var-border-none: \f850; +$fa-var-circle-nodes: \e4e2; +$fa-var-parachute-box: \f4cd; +$fa-var-indent: \f03c; +$fa-var-truck-field-un: \e58e; +$fa-var-hourglass: \f254; +$fa-var-hourglass-empty: \f254; +$fa-var-mountain: \f6fc; +$fa-var-user-doctor: \f0f0; +$fa-var-user-md: \f0f0; +$fa-var-circle-info: \f05a; +$fa-var-info-circle: \f05a; +$fa-var-cloud-meatball: \f73b; +$fa-var-camera: \f030; +$fa-var-camera-alt: \f030; +$fa-var-square-virus: \e578; +$fa-var-meteor: \f753; +$fa-var-car-on: \e4dd; +$fa-var-sleigh: \f7cc; +$fa-var-arrow-down-1-9: \f162; +$fa-var-sort-numeric-asc: \f162; +$fa-var-sort-numeric-down: \f162; +$fa-var-hand-holding-droplet: \f4c1; +$fa-var-hand-holding-water: \f4c1; +$fa-var-water: \f773; +$fa-var-calendar-check: \f274; +$fa-var-braille: \f2a1; +$fa-var-prescription-bottle-medical: \f486; +$fa-var-prescription-bottle-alt: \f486; +$fa-var-landmark: \f66f; +$fa-var-truck: \f0d1; +$fa-var-crosshairs: \f05b; +$fa-var-person-cane: \e53c; +$fa-var-tent: \e57d; +$fa-var-vest-patches: \e086; +$fa-var-check-double: \f560; +$fa-var-arrow-down-a-z: \f15d; +$fa-var-sort-alpha-asc: \f15d; +$fa-var-sort-alpha-down: \f15d; +$fa-var-money-bill-wheat: \e52a; +$fa-var-cookie: \f563; +$fa-var-arrow-rotate-left: \f0e2; +$fa-var-arrow-left-rotate: \f0e2; +$fa-var-arrow-rotate-back: \f0e2; +$fa-var-arrow-rotate-backward: \f0e2; +$fa-var-undo: \f0e2; +$fa-var-hard-drive: \f0a0; +$fa-var-hdd: \f0a0; +$fa-var-face-grin-squint-tears: \f586; +$fa-var-grin-squint-tears: \f586; +$fa-var-dumbbell: \f44b; +$fa-var-rectangle-list: \f022; +$fa-var-list-alt: \f022; +$fa-var-tarp-droplet: \e57c; +$fa-var-house-medical-circle-check: \e511; +$fa-var-person-skiing-nordic: \f7ca; +$fa-var-skiing-nordic: \f7ca; +$fa-var-calendar-plus: \f271; +$fa-var-plane-arrival: \f5af; +$fa-var-circle-left: \f359; +$fa-var-arrow-alt-circle-left: \f359; +$fa-var-train-subway: \f239; +$fa-var-subway: \f239; +$fa-var-chart-gantt: \e0e4; +$fa-var-indian-rupee-sign: \e1bc; +$fa-var-indian-rupee: \e1bc; +$fa-var-inr: \e1bc; +$fa-var-crop-simple: \f565; +$fa-var-crop-alt: \f565; +$fa-var-money-bill-1: \f3d1; +$fa-var-money-bill-alt: \f3d1; +$fa-var-left-long: \f30a; +$fa-var-long-arrow-alt-left: \f30a; +$fa-var-dna: \f471; +$fa-var-virus-slash: \e075; +$fa-var-minus: \f068; +$fa-var-subtract: \f068; +$fa-var-chess: \f439; +$fa-var-arrow-left-long: \f177; +$fa-var-long-arrow-left: \f177; +$fa-var-plug-circle-check: \e55c; +$fa-var-street-view: \f21d; +$fa-var-franc-sign: \e18f; +$fa-var-volume-off: \f026; +$fa-var-hands-asl-interpreting: \f2a3; +$fa-var-american-sign-language-interpreting: \f2a3; +$fa-var-asl-interpreting: \f2a3; +$fa-var-hands-american-sign-language-interpreting: \f2a3; +$fa-var-gear: \f013; +$fa-var-cog: \f013; +$fa-var-droplet-slash: \f5c7; +$fa-var-tint-slash: \f5c7; +$fa-var-mosque: \f678; +$fa-var-mosquito: \e52b; +$fa-var-star-of-david: \f69a; +$fa-var-person-military-rifle: \e54b; +$fa-var-cart-shopping: \f07a; +$fa-var-shopping-cart: \f07a; +$fa-var-vials: \f493; +$fa-var-plug-circle-plus: \e55f; +$fa-var-place-of-worship: \f67f; +$fa-var-grip-vertical: \f58e; +$fa-var-arrow-turn-up: \f148; +$fa-var-level-up: \f148; +$fa-var-u: \55; +$fa-var-square-root-variable: \f698; +$fa-var-square-root-alt: \f698; +$fa-var-clock: \f017; +$fa-var-clock-four: \f017; +$fa-var-backward-step: \f048; +$fa-var-step-backward: \f048; +$fa-var-pallet: \f482; +$fa-var-faucet: \e005; +$fa-var-baseball-bat-ball: \f432; +$fa-var-s: \53; +$fa-var-timeline: \e29c; +$fa-var-keyboard: \f11c; +$fa-var-caret-down: \f0d7; +$fa-var-house-chimney-medical: \f7f2; +$fa-var-clinic-medical: \f7f2; +$fa-var-temperature-three-quarters: \f2c8; +$fa-var-temperature-3: \f2c8; +$fa-var-thermometer-3: \f2c8; +$fa-var-thermometer-three-quarters: \f2c8; +$fa-var-mobile-screen: \f3cf; +$fa-var-mobile-android-alt: \f3cf; +$fa-var-plane-up: \e22d; +$fa-var-piggy-bank: \f4d3; +$fa-var-battery-half: \f242; +$fa-var-battery-3: \f242; +$fa-var-mountain-city: \e52e; +$fa-var-coins: \f51e; +$fa-var-khanda: \f66d; +$fa-var-sliders: \f1de; +$fa-var-sliders-h: \f1de; +$fa-var-folder-tree: \f802; +$fa-var-network-wired: \f6ff; +$fa-var-map-pin: \f276; +$fa-var-hamsa: \f665; +$fa-var-cent-sign: \e3f5; +$fa-var-flask: \f0c3; +$fa-var-person-pregnant: \e31e; +$fa-var-wand-sparkles: \f72b; +$fa-var-ellipsis-vertical: \f142; +$fa-var-ellipsis-v: \f142; +$fa-var-ticket: \f145; +$fa-var-power-off: \f011; +$fa-var-right-long: \f30b; +$fa-var-long-arrow-alt-right: \f30b; +$fa-var-flag-usa: \f74d; +$fa-var-laptop-file: \e51d; +$fa-var-tty: \f1e4; +$fa-var-teletype: \f1e4; +$fa-var-diagram-next: \e476; +$fa-var-person-rifle: \e54e; +$fa-var-house-medical-circle-exclamation: \e512; +$fa-var-closed-captioning: \f20a; +$fa-var-person-hiking: \f6ec; +$fa-var-hiking: \f6ec; +$fa-var-venus-double: \f226; +$fa-var-images: \f302; +$fa-var-calculator: \f1ec; +$fa-var-people-pulling: \e535; +$fa-var-n: \4e; +$fa-var-cable-car: \f7da; +$fa-var-tram: \f7da; +$fa-var-cloud-rain: \f73d; +$fa-var-building-circle-xmark: \e4d4; +$fa-var-ship: \f21a; +$fa-var-arrows-down-to-line: \e4b8; +$fa-var-download: \f019; +$fa-var-face-grin: \f580; +$fa-var-grin: \f580; +$fa-var-delete-left: \f55a; +$fa-var-backspace: \f55a; +$fa-var-eye-dropper: \f1fb; +$fa-var-eye-dropper-empty: \f1fb; +$fa-var-eyedropper: \f1fb; +$fa-var-file-circle-check: \e5a0; +$fa-var-forward: \f04e; +$fa-var-mobile: \f3ce; +$fa-var-mobile-android: \f3ce; +$fa-var-mobile-phone: \f3ce; +$fa-var-face-meh: \f11a; +$fa-var-meh: \f11a; +$fa-var-align-center: \f037; +$fa-var-book-skull: \f6b7; +$fa-var-book-dead: \f6b7; +$fa-var-id-card: \f2c2; +$fa-var-drivers-license: \f2c2; +$fa-var-outdent: \f03b; +$fa-var-dedent: \f03b; +$fa-var-heart-circle-exclamation: \e4fe; +$fa-var-house: \f015; +$fa-var-home: \f015; +$fa-var-home-alt: \f015; +$fa-var-home-lg-alt: \f015; +$fa-var-calendar-week: \f784; +$fa-var-laptop-medical: \f812; +$fa-var-b: \42; +$fa-var-file-medical: \f477; +$fa-var-dice-one: \f525; +$fa-var-kiwi-bird: \f535; +$fa-var-arrow-right-arrow-left: \f0ec; +$fa-var-exchange: \f0ec; +$fa-var-rotate-right: \f2f9; +$fa-var-redo-alt: \f2f9; +$fa-var-rotate-forward: \f2f9; +$fa-var-utensils: \f2e7; +$fa-var-cutlery: \f2e7; +$fa-var-arrow-up-wide-short: \f161; +$fa-var-sort-amount-up: \f161; +$fa-var-mill-sign: \e1ed; +$fa-var-bowl-rice: \e2eb; +$fa-var-skull: \f54c; +$fa-var-tower-broadcast: \f519; +$fa-var-broadcast-tower: \f519; +$fa-var-truck-pickup: \f63c; +$fa-var-up-long: \f30c; +$fa-var-long-arrow-alt-up: \f30c; +$fa-var-stop: \f04d; +$fa-var-code-merge: \f387; +$fa-var-upload: \f093; +$fa-var-hurricane: \f751; +$fa-var-mound: \e52d; +$fa-var-toilet-portable: \e583; +$fa-var-compact-disc: \f51f; +$fa-var-file-arrow-down: \f56d; +$fa-var-file-download: \f56d; +$fa-var-caravan: \f8ff; +$fa-var-shield-cat: \e572; +$fa-var-bolt: \f0e7; +$fa-var-zap: \f0e7; +$fa-var-glass-water: \e4f4; +$fa-var-oil-well: \e532; +$fa-var-vault: \e2c5; +$fa-var-mars: \f222; +$fa-var-toilet: \f7d8; +$fa-var-plane-circle-xmark: \e557; +$fa-var-yen-sign: \f157; +$fa-var-cny: \f157; +$fa-var-jpy: \f157; +$fa-var-rmb: \f157; +$fa-var-yen: \f157; +$fa-var-ruble-sign: \f158; +$fa-var-rouble: \f158; +$fa-var-rub: \f158; +$fa-var-ruble: \f158; +$fa-var-sun: \f185; +$fa-var-guitar: \f7a6; +$fa-var-face-laugh-wink: \f59c; +$fa-var-laugh-wink: \f59c; +$fa-var-horse-head: \f7ab; +$fa-var-bore-hole: \e4c3; +$fa-var-industry: \f275; +$fa-var-circle-down: \f358; +$fa-var-arrow-alt-circle-down: \f358; +$fa-var-arrows-turn-to-dots: \e4c1; +$fa-var-florin-sign: \e184; +$fa-var-arrow-down-short-wide: \f884; +$fa-var-sort-amount-desc: \f884; +$fa-var-sort-amount-down-alt: \f884; +$fa-var-less-than: \3c; +$fa-var-angle-down: \f107; +$fa-var-car-tunnel: \e4de; +$fa-var-head-side-cough: \e061; +$fa-var-grip-lines: \f7a4; +$fa-var-thumbs-down: \f165; +$fa-var-user-lock: \f502; +$fa-var-arrow-right-long: \f178; +$fa-var-long-arrow-right: \f178; +$fa-var-anchor-circle-xmark: \e4ac; +$fa-var-ellipsis: \f141; +$fa-var-ellipsis-h: \f141; +$fa-var-chess-pawn: \f443; +$fa-var-kit-medical: \f479; +$fa-var-first-aid: \f479; +$fa-var-person-through-window: \e5a9; +$fa-var-toolbox: \f552; +$fa-var-hands-holding-circle: \e4fb; +$fa-var-bug: \f188; +$fa-var-credit-card: \f09d; +$fa-var-credit-card-alt: \f09d; +$fa-var-car: \f1b9; +$fa-var-automobile: \f1b9; +$fa-var-hand-holding-hand: \e4f7; +$fa-var-book-open-reader: \f5da; +$fa-var-book-reader: \f5da; +$fa-var-mountain-sun: \e52f; +$fa-var-arrows-left-right-to-line: \e4ba; +$fa-var-dice-d20: \f6cf; +$fa-var-truck-droplet: \e58c; +$fa-var-file-circle-xmark: \e5a1; +$fa-var-temperature-arrow-up: \e040; +$fa-var-temperature-up: \e040; +$fa-var-medal: \f5a2; +$fa-var-bed: \f236; +$fa-var-square-h: \f0fd; +$fa-var-h-square: \f0fd; +$fa-var-podcast: \f2ce; +$fa-var-temperature-full: \f2c7; +$fa-var-temperature-4: \f2c7; +$fa-var-thermometer-4: \f2c7; +$fa-var-thermometer-full: \f2c7; +$fa-var-bell: \f0f3; +$fa-var-superscript: \f12b; +$fa-var-plug-circle-xmark: \e560; +$fa-var-star-of-life: \f621; +$fa-var-phone-slash: \f3dd; +$fa-var-paint-roller: \f5aa; +$fa-var-handshake-angle: \f4c4; +$fa-var-hands-helping: \f4c4; +$fa-var-location-dot: \f3c5; +$fa-var-map-marker-alt: \f3c5; +$fa-var-file: \f15b; +$fa-var-greater-than: \3e; +$fa-var-person-swimming: \f5c4; +$fa-var-swimmer: \f5c4; +$fa-var-arrow-down: \f063; +$fa-var-droplet: \f043; +$fa-var-tint: \f043; +$fa-var-eraser: \f12d; +$fa-var-earth-americas: \f57d; +$fa-var-earth: \f57d; +$fa-var-earth-america: \f57d; +$fa-var-globe-americas: \f57d; +$fa-var-person-burst: \e53b; +$fa-var-dove: \f4ba; +$fa-var-battery-empty: \f244; +$fa-var-battery-0: \f244; +$fa-var-socks: \f696; +$fa-var-inbox: \f01c; +$fa-var-section: \e447; +$fa-var-gauge-high: \f625; +$fa-var-tachometer-alt: \f625; +$fa-var-tachometer-alt-fast: \f625; +$fa-var-envelope-open-text: \f658; +$fa-var-hospital: \f0f8; +$fa-var-hospital-alt: \f0f8; +$fa-var-hospital-wide: \f0f8; +$fa-var-wine-bottle: \f72f; +$fa-var-chess-rook: \f447; +$fa-var-bars-staggered: \f550; +$fa-var-reorder: \f550; +$fa-var-stream: \f550; +$fa-var-dharmachakra: \f655; +$fa-var-hotdog: \f80f; +$fa-var-person-walking-with-cane: \f29d; +$fa-var-blind: \f29d; +$fa-var-drum: \f569; +$fa-var-ice-cream: \f810; +$fa-var-heart-circle-bolt: \e4fc; +$fa-var-fax: \f1ac; +$fa-var-paragraph: \f1dd; +$fa-var-check-to-slot: \f772; +$fa-var-vote-yea: \f772; +$fa-var-star-half: \f089; +$fa-var-boxes-stacked: \f468; +$fa-var-boxes: \f468; +$fa-var-boxes-alt: \f468; +$fa-var-link: \f0c1; +$fa-var-chain: \f0c1; +$fa-var-ear-listen: \f2a2; +$fa-var-assistive-listening-systems: \f2a2; +$fa-var-tree-city: \e587; +$fa-var-play: \f04b; +$fa-var-font: \f031; +$fa-var-rupiah-sign: \e23d; +$fa-var-magnifying-glass: \f002; +$fa-var-search: \f002; +$fa-var-table-tennis-paddle-ball: \f45d; +$fa-var-ping-pong-paddle-ball: \f45d; +$fa-var-table-tennis: \f45d; +$fa-var-person-dots-from-line: \f470; +$fa-var-diagnoses: \f470; +$fa-var-trash-can-arrow-up: \f82a; +$fa-var-trash-restore-alt: \f82a; +$fa-var-naira-sign: \e1f6; +$fa-var-cart-arrow-down: \f218; +$fa-var-walkie-talkie: \f8ef; +$fa-var-file-pen: \f31c; +$fa-var-file-edit: \f31c; +$fa-var-receipt: \f543; +$fa-var-square-pen: \f14b; +$fa-var-pen-square: \f14b; +$fa-var-pencil-square: \f14b; +$fa-var-suitcase-rolling: \f5c1; +$fa-var-person-circle-exclamation: \e53f; +$fa-var-chevron-down: \f078; +$fa-var-battery-full: \f240; +$fa-var-battery: \f240; +$fa-var-battery-5: \f240; +$fa-var-skull-crossbones: \f714; +$fa-var-code-compare: \e13a; +$fa-var-list-ul: \f0ca; +$fa-var-list-dots: \f0ca; +$fa-var-school-lock: \e56f; +$fa-var-tower-cell: \e585; +$fa-var-down-long: \f309; +$fa-var-long-arrow-alt-down: \f309; +$fa-var-ranking-star: \e561; +$fa-var-chess-king: \f43f; +$fa-var-person-harassing: \e549; +$fa-var-brazilian-real-sign: \e46c; +$fa-var-landmark-dome: \f752; +$fa-var-landmark-alt: \f752; +$fa-var-arrow-up: \f062; +$fa-var-tv: \f26c; +$fa-var-television: \f26c; +$fa-var-tv-alt: \f26c; +$fa-var-shrimp: \e448; +$fa-var-list-check: \f0ae; +$fa-var-tasks: \f0ae; +$fa-var-jug-detergent: \e519; +$fa-var-circle-user: \f2bd; +$fa-var-user-circle: \f2bd; +$fa-var-user-shield: \f505; +$fa-var-wind: \f72e; +$fa-var-car-burst: \f5e1; +$fa-var-car-crash: \f5e1; +$fa-var-y: \59; +$fa-var-person-snowboarding: \f7ce; +$fa-var-snowboarding: \f7ce; +$fa-var-truck-fast: \f48b; +$fa-var-shipping-fast: \f48b; +$fa-var-fish: \f578; +$fa-var-user-graduate: \f501; +$fa-var-circle-half-stroke: \f042; +$fa-var-adjust: \f042; +$fa-var-clapperboard: \e131; +$fa-var-circle-radiation: \f7ba; +$fa-var-radiation-alt: \f7ba; +$fa-var-baseball: \f433; +$fa-var-baseball-ball: \f433; +$fa-var-jet-fighter-up: \e518; +$fa-var-diagram-project: \f542; +$fa-var-project-diagram: \f542; +$fa-var-copy: \f0c5; +$fa-var-volume-xmark: \f6a9; +$fa-var-volume-mute: \f6a9; +$fa-var-volume-times: \f6a9; +$fa-var-hand-sparkles: \e05d; +$fa-var-grip: \f58d; +$fa-var-grip-horizontal: \f58d; +$fa-var-share-from-square: \f14d; +$fa-var-share-square: \f14d; +$fa-var-child-combatant: \e4e0; +$fa-var-child-rifle: \e4e0; +$fa-var-gun: \e19b; +$fa-var-square-phone: \f098; +$fa-var-phone-square: \f098; +$fa-var-plus: \2b; +$fa-var-add: \2b; +$fa-var-expand: \f065; +$fa-var-computer: \e4e5; +$fa-var-xmark: \f00d; +$fa-var-close: \f00d; +$fa-var-multiply: \f00d; +$fa-var-remove: \f00d; +$fa-var-times: \f00d; +$fa-var-arrows-up-down-left-right: \f047; +$fa-var-arrows: \f047; +$fa-var-chalkboard-user: \f51c; +$fa-var-chalkboard-teacher: \f51c; +$fa-var-peso-sign: \e222; +$fa-var-building-shield: \e4d8; +$fa-var-baby: \f77c; +$fa-var-users-line: \e592; +$fa-var-quote-left: \f10d; +$fa-var-quote-left-alt: \f10d; +$fa-var-tractor: \f722; +$fa-var-trash-arrow-up: \f829; +$fa-var-trash-restore: \f829; +$fa-var-arrow-down-up-lock: \e4b0; +$fa-var-lines-leaning: \e51e; +$fa-var-ruler-combined: \f546; +$fa-var-copyright: \f1f9; +$fa-var-equals: \3d; +$fa-var-blender: \f517; +$fa-var-teeth: \f62e; +$fa-var-shekel-sign: \f20b; +$fa-var-ils: \f20b; +$fa-var-shekel: \f20b; +$fa-var-sheqel: \f20b; +$fa-var-sheqel-sign: \f20b; +$fa-var-map: \f279; +$fa-var-rocket: \f135; +$fa-var-photo-film: \f87c; +$fa-var-photo-video: \f87c; +$fa-var-folder-minus: \f65d; +$fa-var-store: \f54e; +$fa-var-arrow-trend-up: \e098; +$fa-var-plug-circle-minus: \e55e; +$fa-var-sign-hanging: \f4d9; +$fa-var-sign: \f4d9; +$fa-var-bezier-curve: \f55b; +$fa-var-bell-slash: \f1f6; +$fa-var-tablet: \f3fb; +$fa-var-tablet-android: \f3fb; +$fa-var-school-flag: \e56e; +$fa-var-fill: \f575; +$fa-var-angle-up: \f106; +$fa-var-drumstick-bite: \f6d7; +$fa-var-holly-berry: \f7aa; +$fa-var-chevron-left: \f053; +$fa-var-bacteria: \e059; +$fa-var-hand-lizard: \f258; +$fa-var-notdef: \e1fe; +$fa-var-disease: \f7fa; +$fa-var-briefcase-medical: \f469; +$fa-var-genderless: \f22d; +$fa-var-chevron-right: \f054; +$fa-var-retweet: \f079; +$fa-var-car-rear: \f5de; +$fa-var-car-alt: \f5de; +$fa-var-pump-soap: \e06b; +$fa-var-video-slash: \f4e2; +$fa-var-battery-quarter: \f243; +$fa-var-battery-2: \f243; +$fa-var-radio: \f8d7; +$fa-var-baby-carriage: \f77d; +$fa-var-carriage-baby: \f77d; +$fa-var-traffic-light: \f637; +$fa-var-thermometer: \f491; +$fa-var-vr-cardboard: \f729; +$fa-var-hand-middle-finger: \f806; +$fa-var-percent: \25; +$fa-var-percentage: \25; +$fa-var-truck-moving: \f4df; +$fa-var-glass-water-droplet: \e4f5; +$fa-var-display: \e163; +$fa-var-face-smile: \f118; +$fa-var-smile: \f118; +$fa-var-thumbtack: \f08d; +$fa-var-thumb-tack: \f08d; +$fa-var-trophy: \f091; +$fa-var-person-praying: \f683; +$fa-var-pray: \f683; +$fa-var-hammer: \f6e3; +$fa-var-hand-peace: \f25b; +$fa-var-rotate: \f2f1; +$fa-var-sync-alt: \f2f1; +$fa-var-spinner: \f110; +$fa-var-robot: \f544; +$fa-var-peace: \f67c; +$fa-var-gears: \f085; +$fa-var-cogs: \f085; +$fa-var-warehouse: \f494; +$fa-var-arrow-up-right-dots: \e4b7; +$fa-var-splotch: \f5bc; +$fa-var-face-grin-hearts: \f584; +$fa-var-grin-hearts: \f584; +$fa-var-dice-four: \f524; +$fa-var-sim-card: \f7c4; +$fa-var-transgender: \f225; +$fa-var-transgender-alt: \f225; +$fa-var-mercury: \f223; +$fa-var-arrow-turn-down: \f149; +$fa-var-level-down: \f149; +$fa-var-person-falling-burst: \e547; +$fa-var-award: \f559; +$fa-var-ticket-simple: \f3ff; +$fa-var-ticket-alt: \f3ff; +$fa-var-building: \f1ad; +$fa-var-angles-left: \f100; +$fa-var-angle-double-left: \f100; +$fa-var-qrcode: \f029; +$fa-var-clock-rotate-left: \f1da; +$fa-var-history: \f1da; +$fa-var-face-grin-beam-sweat: \f583; +$fa-var-grin-beam-sweat: \f583; +$fa-var-file-export: \f56e; +$fa-var-arrow-right-from-file: \f56e; +$fa-var-shield: \f132; +$fa-var-shield-blank: \f132; +$fa-var-arrow-up-short-wide: \f885; +$fa-var-sort-amount-up-alt: \f885; +$fa-var-house-medical: \e3b2; +$fa-var-golf-ball-tee: \f450; +$fa-var-golf-ball: \f450; +$fa-var-circle-chevron-left: \f137; +$fa-var-chevron-circle-left: \f137; +$fa-var-house-chimney-window: \e00d; +$fa-var-pen-nib: \f5ad; +$fa-var-tent-arrow-turn-left: \e580; +$fa-var-tents: \e582; +$fa-var-wand-magic: \f0d0; +$fa-var-magic: \f0d0; +$fa-var-dog: \f6d3; +$fa-var-carrot: \f787; +$fa-var-moon: \f186; +$fa-var-wine-glass-empty: \f5ce; +$fa-var-wine-glass-alt: \f5ce; +$fa-var-cheese: \f7ef; +$fa-var-yin-yang: \f6ad; +$fa-var-music: \f001; +$fa-var-code-commit: \f386; +$fa-var-temperature-low: \f76b; +$fa-var-person-biking: \f84a; +$fa-var-biking: \f84a; +$fa-var-broom: \f51a; +$fa-var-shield-heart: \e574; +$fa-var-gopuram: \f664; +$fa-var-earth-oceania: \e47b; +$fa-var-globe-oceania: \e47b; +$fa-var-square-xmark: \f2d3; +$fa-var-times-square: \f2d3; +$fa-var-xmark-square: \f2d3; +$fa-var-hashtag: \23; +$fa-var-up-right-and-down-left-from-center: \f424; +$fa-var-expand-alt: \f424; +$fa-var-oil-can: \f613; +$fa-var-t: \54; +$fa-var-hippo: \f6ed; +$fa-var-chart-column: \e0e3; +$fa-var-infinity: \f534; +$fa-var-vial-circle-check: \e596; +$fa-var-person-arrow-down-to-line: \e538; +$fa-var-voicemail: \f897; +$fa-var-fan: \f863; +$fa-var-person-walking-luggage: \e554; +$fa-var-up-down: \f338; +$fa-var-arrows-alt-v: \f338; +$fa-var-cloud-moon-rain: \f73c; +$fa-var-calendar: \f133; +$fa-var-trailer: \e041; +$fa-var-bahai: \f666; +$fa-var-haykal: \f666; +$fa-var-sd-card: \f7c2; +$fa-var-dragon: \f6d5; +$fa-var-shoe-prints: \f54b; +$fa-var-circle-plus: \f055; +$fa-var-plus-circle: \f055; +$fa-var-face-grin-tongue-wink: \f58b; +$fa-var-grin-tongue-wink: \f58b; +$fa-var-hand-holding: \f4bd; +$fa-var-plug-circle-exclamation: \e55d; +$fa-var-link-slash: \f127; +$fa-var-chain-broken: \f127; +$fa-var-chain-slash: \f127; +$fa-var-unlink: \f127; +$fa-var-clone: \f24d; +$fa-var-person-walking-arrow-loop-left: \e551; +$fa-var-arrow-up-z-a: \f882; +$fa-var-sort-alpha-up-alt: \f882; +$fa-var-fire-flame-curved: \f7e4; +$fa-var-fire-alt: \f7e4; +$fa-var-tornado: \f76f; +$fa-var-file-circle-plus: \e494; +$fa-var-book-quran: \f687; +$fa-var-quran: \f687; +$fa-var-anchor: \f13d; +$fa-var-border-all: \f84c; +$fa-var-face-angry: \f556; +$fa-var-angry: \f556; +$fa-var-cookie-bite: \f564; +$fa-var-arrow-trend-down: \e097; +$fa-var-rss: \f09e; +$fa-var-feed: \f09e; +$fa-var-draw-polygon: \f5ee; +$fa-var-scale-balanced: \f24e; +$fa-var-balance-scale: \f24e; +$fa-var-gauge-simple-high: \f62a; +$fa-var-tachometer: \f62a; +$fa-var-tachometer-fast: \f62a; +$fa-var-shower: \f2cc; +$fa-var-desktop: \f390; +$fa-var-desktop-alt: \f390; +$fa-var-m: \4d; +$fa-var-table-list: \f00b; +$fa-var-th-list: \f00b; +$fa-var-comment-sms: \f7cd; +$fa-var-sms: \f7cd; +$fa-var-book: \f02d; +$fa-var-user-plus: \f234; +$fa-var-check: \f00c; +$fa-var-battery-three-quarters: \f241; +$fa-var-battery-4: \f241; +$fa-var-house-circle-check: \e509; +$fa-var-angle-left: \f104; +$fa-var-diagram-successor: \e47a; +$fa-var-truck-arrow-right: \e58b; +$fa-var-arrows-split-up-and-left: \e4bc; +$fa-var-hand-fist: \f6de; +$fa-var-fist-raised: \f6de; +$fa-var-cloud-moon: \f6c3; +$fa-var-briefcase: \f0b1; +$fa-var-person-falling: \e546; +$fa-var-image-portrait: \f3e0; +$fa-var-portrait: \f3e0; +$fa-var-user-tag: \f507; +$fa-var-rug: \e569; +$fa-var-earth-europe: \f7a2; +$fa-var-globe-europe: \f7a2; +$fa-var-cart-flatbed-suitcase: \f59d; +$fa-var-luggage-cart: \f59d; +$fa-var-rectangle-xmark: \f410; +$fa-var-rectangle-times: \f410; +$fa-var-times-rectangle: \f410; +$fa-var-window-close: \f410; +$fa-var-baht-sign: \e0ac; +$fa-var-book-open: \f518; +$fa-var-book-journal-whills: \f66a; +$fa-var-journal-whills: \f66a; +$fa-var-handcuffs: \e4f8; +$fa-var-triangle-exclamation: \f071; +$fa-var-exclamation-triangle: \f071; +$fa-var-warning: \f071; +$fa-var-database: \f1c0; +$fa-var-share: \f064; +$fa-var-arrow-turn-right: \f064; +$fa-var-mail-forward: \f064; +$fa-var-bottle-droplet: \e4c4; +$fa-var-mask-face: \e1d7; +$fa-var-hill-rockslide: \e508; +$fa-var-right-left: \f362; +$fa-var-exchange-alt: \f362; +$fa-var-paper-plane: \f1d8; +$fa-var-road-circle-exclamation: \e565; +$fa-var-dungeon: \f6d9; +$fa-var-align-right: \f038; +$fa-var-money-bill-1-wave: \f53b; +$fa-var-money-bill-wave-alt: \f53b; +$fa-var-life-ring: \f1cd; +$fa-var-hands: \f2a7; +$fa-var-sign-language: \f2a7; +$fa-var-signing: \f2a7; +$fa-var-calendar-day: \f783; +$fa-var-water-ladder: \f5c5; +$fa-var-ladder-water: \f5c5; +$fa-var-swimming-pool: \f5c5; +$fa-var-arrows-up-down: \f07d; +$fa-var-arrows-v: \f07d; +$fa-var-face-grimace: \f57f; +$fa-var-grimace: \f57f; +$fa-var-wheelchair-move: \e2ce; +$fa-var-wheelchair-alt: \e2ce; +$fa-var-turn-down: \f3be; +$fa-var-level-down-alt: \f3be; +$fa-var-person-walking-arrow-right: \e552; +$fa-var-square-envelope: \f199; +$fa-var-envelope-square: \f199; +$fa-var-dice: \f522; +$fa-var-bowling-ball: \f436; +$fa-var-brain: \f5dc; +$fa-var-bandage: \f462; +$fa-var-band-aid: \f462; +$fa-var-calendar-minus: \f272; +$fa-var-circle-xmark: \f057; +$fa-var-times-circle: \f057; +$fa-var-xmark-circle: \f057; +$fa-var-gifts: \f79c; +$fa-var-hotel: \f594; +$fa-var-earth-asia: \f57e; +$fa-var-globe-asia: \f57e; +$fa-var-id-card-clip: \f47f; +$fa-var-id-card-alt: \f47f; +$fa-var-magnifying-glass-plus: \f00e; +$fa-var-search-plus: \f00e; +$fa-var-thumbs-up: \f164; +$fa-var-user-clock: \f4fd; +$fa-var-hand-dots: \f461; +$fa-var-allergies: \f461; +$fa-var-file-invoice: \f570; +$fa-var-window-minimize: \f2d1; +$fa-var-mug-saucer: \f0f4; +$fa-var-coffee: \f0f4; +$fa-var-brush: \f55d; +$fa-var-mask: \f6fa; +$fa-var-magnifying-glass-minus: \f010; +$fa-var-search-minus: \f010; +$fa-var-ruler-vertical: \f548; +$fa-var-user-large: \f406; +$fa-var-user-alt: \f406; +$fa-var-train-tram: \e5b4; +$fa-var-user-nurse: \f82f; +$fa-var-syringe: \f48e; +$fa-var-cloud-sun: \f6c4; +$fa-var-stopwatch-20: \e06f; +$fa-var-square-full: \f45c; +$fa-var-magnet: \f076; +$fa-var-jar: \e516; +$fa-var-note-sticky: \f249; +$fa-var-sticky-note: \f249; +$fa-var-bug-slash: \e490; +$fa-var-arrow-up-from-water-pump: \e4b6; +$fa-var-bone: \f5d7; +$fa-var-user-injured: \f728; +$fa-var-face-sad-tear: \f5b4; +$fa-var-sad-tear: \f5b4; +$fa-var-plane: \f072; +$fa-var-tent-arrows-down: \e581; +$fa-var-exclamation: \21; +$fa-var-arrows-spin: \e4bb; +$fa-var-print: \f02f; +$fa-var-turkish-lira-sign: \e2bb; +$fa-var-try: \e2bb; +$fa-var-turkish-lira: \e2bb; +$fa-var-dollar-sign: \24; +$fa-var-dollar: \24; +$fa-var-usd: \24; +$fa-var-x: \58; +$fa-var-magnifying-glass-dollar: \f688; +$fa-var-search-dollar: \f688; +$fa-var-users-gear: \f509; +$fa-var-users-cog: \f509; +$fa-var-person-military-pointing: \e54a; +$fa-var-building-columns: \f19c; +$fa-var-bank: \f19c; +$fa-var-institution: \f19c; +$fa-var-museum: \f19c; +$fa-var-university: \f19c; +$fa-var-umbrella: \f0e9; +$fa-var-trowel: \e589; +$fa-var-d: \44; +$fa-var-stapler: \e5af; +$fa-var-masks-theater: \f630; +$fa-var-theater-masks: \f630; +$fa-var-kip-sign: \e1c4; +$fa-var-hand-point-left: \f0a5; +$fa-var-handshake-simple: \f4c6; +$fa-var-handshake-alt: \f4c6; +$fa-var-jet-fighter: \f0fb; +$fa-var-fighter-jet: \f0fb; +$fa-var-square-share-nodes: \f1e1; +$fa-var-share-alt-square: \f1e1; +$fa-var-barcode: \f02a; +$fa-var-plus-minus: \e43c; +$fa-var-video: \f03d; +$fa-var-video-camera: \f03d; +$fa-var-graduation-cap: \f19d; +$fa-var-mortar-board: \f19d; +$fa-var-hand-holding-medical: \e05c; +$fa-var-person-circle-check: \e53e; +$fa-var-turn-up: \f3bf; +$fa-var-level-up-alt: \f3bf; + +$fa-var-monero: \f3d0; +$fa-var-hooli: \f427; +$fa-var-yelp: \f1e9; +$fa-var-cc-visa: \f1f0; +$fa-var-lastfm: \f202; +$fa-var-shopware: \f5b5; +$fa-var-creative-commons-nc: \f4e8; +$fa-var-aws: \f375; +$fa-var-redhat: \f7bc; +$fa-var-yoast: \f2b1; +$fa-var-cloudflare: \e07d; +$fa-var-ups: \f7e0; +$fa-var-wpexplorer: \f2de; +$fa-var-dyalog: \f399; +$fa-var-bity: \f37a; +$fa-var-stackpath: \f842; +$fa-var-buysellads: \f20d; +$fa-var-first-order: \f2b0; +$fa-var-modx: \f285; +$fa-var-guilded: \e07e; +$fa-var-vnv: \f40b; +$fa-var-square-js: \f3b9; +$fa-var-js-square: \f3b9; +$fa-var-microsoft: \f3ca; +$fa-var-qq: \f1d6; +$fa-var-orcid: \f8d2; +$fa-var-java: \f4e4; +$fa-var-invision: \f7b0; +$fa-var-creative-commons-pd-alt: \f4ed; +$fa-var-centercode: \f380; +$fa-var-glide-g: \f2a6; +$fa-var-drupal: \f1a9; +$fa-var-hire-a-helper: \f3b0; +$fa-var-creative-commons-by: \f4e7; +$fa-var-unity: \e049; +$fa-var-whmcs: \f40d; +$fa-var-rocketchat: \f3e8; +$fa-var-vk: \f189; +$fa-var-untappd: \f405; +$fa-var-mailchimp: \f59e; +$fa-var-css3-alt: \f38b; +$fa-var-square-reddit: \f1a2; +$fa-var-reddit-square: \f1a2; +$fa-var-vimeo-v: \f27d; +$fa-var-contao: \f26d; +$fa-var-square-font-awesome: \e5ad; +$fa-var-deskpro: \f38f; +$fa-var-sistrix: \f3ee; +$fa-var-square-instagram: \e055; +$fa-var-instagram-square: \e055; +$fa-var-battle-net: \f835; +$fa-var-the-red-yeti: \f69d; +$fa-var-square-hacker-news: \f3af; +$fa-var-hacker-news-square: \f3af; +$fa-var-edge: \f282; +$fa-var-napster: \f3d2; +$fa-var-square-snapchat: \f2ad; +$fa-var-snapchat-square: \f2ad; +$fa-var-google-plus-g: \f0d5; +$fa-var-artstation: \f77a; +$fa-var-markdown: \f60f; +$fa-var-sourcetree: \f7d3; +$fa-var-google-plus: \f2b3; +$fa-var-diaspora: \f791; +$fa-var-foursquare: \f180; +$fa-var-stack-overflow: \f16c; +$fa-var-github-alt: \f113; +$fa-var-phoenix-squadron: \f511; +$fa-var-pagelines: \f18c; +$fa-var-algolia: \f36c; +$fa-var-red-river: \f3e3; +$fa-var-creative-commons-sa: \f4ef; +$fa-var-safari: \f267; +$fa-var-google: \f1a0; +$fa-var-square-font-awesome-stroke: \f35c; +$fa-var-font-awesome-alt: \f35c; +$fa-var-atlassian: \f77b; +$fa-var-linkedin-in: \f0e1; +$fa-var-digital-ocean: \f391; +$fa-var-nimblr: \f5a8; +$fa-var-chromecast: \f838; +$fa-var-evernote: \f839; +$fa-var-hacker-news: \f1d4; +$fa-var-creative-commons-sampling: \f4f0; +$fa-var-adversal: \f36a; +$fa-var-creative-commons: \f25e; +$fa-var-watchman-monitoring: \e087; +$fa-var-fonticons: \f280; +$fa-var-weixin: \f1d7; +$fa-var-shirtsinbulk: \f214; +$fa-var-codepen: \f1cb; +$fa-var-git-alt: \f841; +$fa-var-lyft: \f3c3; +$fa-var-rev: \f5b2; +$fa-var-windows: \f17a; +$fa-var-wizards-of-the-coast: \f730; +$fa-var-square-viadeo: \f2aa; +$fa-var-viadeo-square: \f2aa; +$fa-var-meetup: \f2e0; +$fa-var-centos: \f789; +$fa-var-adn: \f170; +$fa-var-cloudsmith: \f384; +$fa-var-pied-piper-alt: \f1a8; +$fa-var-square-dribbble: \f397; +$fa-var-dribbble-square: \f397; +$fa-var-codiepie: \f284; +$fa-var-node: \f419; +$fa-var-mix: \f3cb; +$fa-var-steam: \f1b6; +$fa-var-cc-apple-pay: \f416; +$fa-var-scribd: \f28a; +$fa-var-openid: \f19b; +$fa-var-instalod: \e081; +$fa-var-expeditedssl: \f23e; +$fa-var-sellcast: \f2da; +$fa-var-square-twitter: \f081; +$fa-var-twitter-square: \f081; +$fa-var-r-project: \f4f7; +$fa-var-delicious: \f1a5; +$fa-var-freebsd: \f3a4; +$fa-var-vuejs: \f41f; +$fa-var-accusoft: \f369; +$fa-var-ioxhost: \f208; +$fa-var-fonticons-fi: \f3a2; +$fa-var-app-store: \f36f; +$fa-var-cc-mastercard: \f1f1; +$fa-var-itunes-note: \f3b5; +$fa-var-golang: \e40f; +$fa-var-kickstarter: \f3bb; +$fa-var-grav: \f2d6; +$fa-var-weibo: \f18a; +$fa-var-uncharted: \e084; +$fa-var-firstdraft: \f3a1; +$fa-var-square-youtube: \f431; +$fa-var-youtube-square: \f431; +$fa-var-wikipedia-w: \f266; +$fa-var-wpressr: \f3e4; +$fa-var-rendact: \f3e4; +$fa-var-angellist: \f209; +$fa-var-galactic-republic: \f50c; +$fa-var-nfc-directional: \e530; +$fa-var-skype: \f17e; +$fa-var-joget: \f3b7; +$fa-var-fedora: \f798; +$fa-var-stripe-s: \f42a; +$fa-var-meta: \e49b; +$fa-var-laravel: \f3bd; +$fa-var-hotjar: \f3b1; +$fa-var-bluetooth-b: \f294; +$fa-var-sticker-mule: \f3f7; +$fa-var-creative-commons-zero: \f4f3; +$fa-var-hips: \f452; +$fa-var-behance: \f1b4; +$fa-var-reddit: \f1a1; +$fa-var-discord: \f392; +$fa-var-chrome: \f268; +$fa-var-app-store-ios: \f370; +$fa-var-cc-discover: \f1f2; +$fa-var-wpbeginner: \f297; +$fa-var-confluence: \f78d; +$fa-var-mdb: \f8ca; +$fa-var-dochub: \f394; +$fa-var-accessible-icon: \f368; +$fa-var-ebay: \f4f4; +$fa-var-amazon: \f270; +$fa-var-unsplash: \e07c; +$fa-var-yarn: \f7e3; +$fa-var-square-steam: \f1b7; +$fa-var-steam-square: \f1b7; +$fa-var-500px: \f26e; +$fa-var-square-vimeo: \f194; +$fa-var-vimeo-square: \f194; +$fa-var-asymmetrik: \f372; +$fa-var-font-awesome: \f2b4; +$fa-var-font-awesome-flag: \f2b4; +$fa-var-font-awesome-logo-full: \f2b4; +$fa-var-gratipay: \f184; +$fa-var-apple: \f179; +$fa-var-hive: \e07f; +$fa-var-gitkraken: \f3a6; +$fa-var-keybase: \f4f5; +$fa-var-apple-pay: \f415; +$fa-var-padlet: \e4a0; +$fa-var-amazon-pay: \f42c; +$fa-var-square-github: \f092; +$fa-var-github-square: \f092; +$fa-var-stumbleupon: \f1a4; +$fa-var-fedex: \f797; +$fa-var-phoenix-framework: \f3dc; +$fa-var-shopify: \e057; +$fa-var-neos: \f612; +$fa-var-hackerrank: \f5f7; +$fa-var-researchgate: \f4f8; +$fa-var-swift: \f8e1; +$fa-var-angular: \f420; +$fa-var-speakap: \f3f3; +$fa-var-angrycreative: \f36e; +$fa-var-y-combinator: \f23b; +$fa-var-empire: \f1d1; +$fa-var-envira: \f299; +$fa-var-square-gitlab: \e5ae; +$fa-var-gitlab-square: \e5ae; +$fa-var-studiovinari: \f3f8; +$fa-var-pied-piper: \f2ae; +$fa-var-wordpress: \f19a; +$fa-var-product-hunt: \f288; +$fa-var-firefox: \f269; +$fa-var-linode: \f2b8; +$fa-var-goodreads: \f3a8; +$fa-var-square-odnoklassniki: \f264; +$fa-var-odnoklassniki-square: \f264; +$fa-var-jsfiddle: \f1cc; +$fa-var-sith: \f512; +$fa-var-themeisle: \f2b2; +$fa-var-page4: \f3d7; +$fa-var-hashnode: \e499; +$fa-var-react: \f41b; +$fa-var-cc-paypal: \f1f4; +$fa-var-squarespace: \f5be; +$fa-var-cc-stripe: \f1f5; +$fa-var-creative-commons-share: \f4f2; +$fa-var-bitcoin: \f379; +$fa-var-keycdn: \f3ba; +$fa-var-opera: \f26a; +$fa-var-itch-io: \f83a; +$fa-var-umbraco: \f8e8; +$fa-var-galactic-senate: \f50d; +$fa-var-ubuntu: \f7df; +$fa-var-draft2digital: \f396; +$fa-var-stripe: \f429; +$fa-var-houzz: \f27c; +$fa-var-gg: \f260; +$fa-var-dhl: \f790; +$fa-var-square-pinterest: \f0d3; +$fa-var-pinterest-square: \f0d3; +$fa-var-xing: \f168; +$fa-var-blackberry: \f37b; +$fa-var-creative-commons-pd: \f4ec; +$fa-var-playstation: \f3df; +$fa-var-quinscape: \f459; +$fa-var-less: \f41d; +$fa-var-blogger-b: \f37d; +$fa-var-opencart: \f23d; +$fa-var-vine: \f1ca; +$fa-var-paypal: \f1ed; +$fa-var-gitlab: \f296; +$fa-var-typo3: \f42b; +$fa-var-reddit-alien: \f281; +$fa-var-yahoo: \f19e; +$fa-var-dailymotion: \e052; +$fa-var-affiliatetheme: \f36b; +$fa-var-pied-piper-pp: \f1a7; +$fa-var-bootstrap: \f836; +$fa-var-odnoklassniki: \f263; +$fa-var-nfc-symbol: \e531; +$fa-var-ethereum: \f42e; +$fa-var-speaker-deck: \f83c; +$fa-var-creative-commons-nc-eu: \f4e9; +$fa-var-patreon: \f3d9; +$fa-var-avianex: \f374; +$fa-var-ello: \f5f1; +$fa-var-gofore: \f3a7; +$fa-var-bimobject: \f378; +$fa-var-facebook-f: \f39e; +$fa-var-square-google-plus: \f0d4; +$fa-var-google-plus-square: \f0d4; +$fa-var-mandalorian: \f50f; +$fa-var-first-order-alt: \f50a; +$fa-var-osi: \f41a; +$fa-var-google-wallet: \f1ee; +$fa-var-d-and-d-beyond: \f6ca; +$fa-var-periscope: \f3da; +$fa-var-fulcrum: \f50b; +$fa-var-cloudscale: \f383; +$fa-var-forumbee: \f211; +$fa-var-mizuni: \f3cc; +$fa-var-schlix: \f3ea; +$fa-var-square-xing: \f169; +$fa-var-xing-square: \f169; +$fa-var-bandcamp: \f2d5; +$fa-var-wpforms: \f298; +$fa-var-cloudversify: \f385; +$fa-var-usps: \f7e1; +$fa-var-megaport: \f5a3; +$fa-var-magento: \f3c4; +$fa-var-spotify: \f1bc; +$fa-var-optin-monster: \f23c; +$fa-var-fly: \f417; +$fa-var-aviato: \f421; +$fa-var-itunes: \f3b4; +$fa-var-cuttlefish: \f38c; +$fa-var-blogger: \f37c; +$fa-var-flickr: \f16e; +$fa-var-viber: \f409; +$fa-var-soundcloud: \f1be; +$fa-var-digg: \f1a6; +$fa-var-tencent-weibo: \f1d5; +$fa-var-symfony: \f83d; +$fa-var-maxcdn: \f136; +$fa-var-etsy: \f2d7; +$fa-var-facebook-messenger: \f39f; +$fa-var-audible: \f373; +$fa-var-think-peaks: \f731; +$fa-var-bilibili: \e3d9; +$fa-var-erlang: \f39d; +$fa-var-cotton-bureau: \f89e; +$fa-var-dashcube: \f210; +$fa-var-42-group: \e080; +$fa-var-innosoft: \e080; +$fa-var-stack-exchange: \f18d; +$fa-var-elementor: \f430; +$fa-var-square-pied-piper: \e01e; +$fa-var-pied-piper-square: \e01e; +$fa-var-creative-commons-nd: \f4eb; +$fa-var-palfed: \f3d8; +$fa-var-superpowers: \f2dd; +$fa-var-resolving: \f3e7; +$fa-var-xbox: \f412; +$fa-var-searchengin: \f3eb; +$fa-var-tiktok: \e07b; +$fa-var-square-facebook: \f082; +$fa-var-facebook-square: \f082; +$fa-var-renren: \f18b; +$fa-var-linux: \f17c; +$fa-var-glide: \f2a5; +$fa-var-linkedin: \f08c; +$fa-var-hubspot: \f3b2; +$fa-var-deploydog: \f38e; +$fa-var-twitch: \f1e8; +$fa-var-ravelry: \f2d9; +$fa-var-mixer: \e056; +$fa-var-square-lastfm: \f203; +$fa-var-lastfm-square: \f203; +$fa-var-vimeo: \f40a; +$fa-var-mendeley: \f7b3; +$fa-var-uniregistry: \f404; +$fa-var-figma: \f799; +$fa-var-creative-commons-remix: \f4ee; +$fa-var-cc-amazon-pay: \f42d; +$fa-var-dropbox: \f16b; +$fa-var-instagram: \f16d; +$fa-var-cmplid: \e360; +$fa-var-facebook: \f09a; +$fa-var-gripfire: \f3ac; +$fa-var-jedi-order: \f50e; +$fa-var-uikit: \f403; +$fa-var-fort-awesome-alt: \f3a3; +$fa-var-phabricator: \f3db; +$fa-var-ussunnah: \f407; +$fa-var-earlybirds: \f39a; +$fa-var-trade-federation: \f513; +$fa-var-autoprefixer: \f41c; +$fa-var-whatsapp: \f232; +$fa-var-slideshare: \f1e7; +$fa-var-google-play: \f3ab; +$fa-var-viadeo: \f2a9; +$fa-var-line: \f3c0; +$fa-var-google-drive: \f3aa; +$fa-var-servicestack: \f3ec; +$fa-var-simplybuilt: \f215; +$fa-var-bitbucket: \f171; +$fa-var-imdb: \f2d8; +$fa-var-deezer: \e077; +$fa-var-raspberry-pi: \f7bb; +$fa-var-jira: \f7b1; +$fa-var-docker: \f395; +$fa-var-screenpal: \e570; +$fa-var-bluetooth: \f293; +$fa-var-gitter: \f426; +$fa-var-d-and-d: \f38d; +$fa-var-microblog: \e01a; +$fa-var-cc-diners-club: \f24c; +$fa-var-gg-circle: \f261; +$fa-var-pied-piper-hat: \f4e5; +$fa-var-kickstarter-k: \f3bc; +$fa-var-yandex: \f413; +$fa-var-readme: \f4d5; +$fa-var-html5: \f13b; +$fa-var-sellsy: \f213; +$fa-var-sass: \f41e; +$fa-var-wirsindhandwerk: \e2d0; +$fa-var-wsh: \e2d0; +$fa-var-buromobelexperte: \f37f; +$fa-var-salesforce: \f83b; +$fa-var-octopus-deploy: \e082; +$fa-var-medapps: \f3c6; +$fa-var-ns8: \f3d5; +$fa-var-pinterest-p: \f231; +$fa-var-apper: \f371; +$fa-var-fort-awesome: \f286; +$fa-var-waze: \f83f; +$fa-var-cc-jcb: \f24b; +$fa-var-snapchat: \f2ab; +$fa-var-snapchat-ghost: \f2ab; +$fa-var-fantasy-flight-games: \f6dc; +$fa-var-rust: \e07a; +$fa-var-wix: \f5cf; +$fa-var-square-behance: \f1b5; +$fa-var-behance-square: \f1b5; +$fa-var-supple: \f3f9; +$fa-var-rebel: \f1d0; +$fa-var-css3: \f13c; +$fa-var-staylinked: \f3f5; +$fa-var-kaggle: \f5fa; +$fa-var-space-awesome: \e5ac; +$fa-var-deviantart: \f1bd; +$fa-var-cpanel: \f388; +$fa-var-goodreads-g: \f3a9; +$fa-var-square-git: \f1d2; +$fa-var-git-square: \f1d2; +$fa-var-square-tumblr: \f174; +$fa-var-tumblr-square: \f174; +$fa-var-trello: \f181; +$fa-var-creative-commons-nc-jp: \f4ea; +$fa-var-get-pocket: \f265; +$fa-var-perbyte: \e083; +$fa-var-grunt: \f3ad; +$fa-var-weebly: \f5cc; +$fa-var-connectdevelop: \f20e; +$fa-var-leanpub: \f212; +$fa-var-black-tie: \f27e; +$fa-var-themeco: \f5c6; +$fa-var-python: \f3e2; +$fa-var-android: \f17b; +$fa-var-bots: \e340; +$fa-var-free-code-camp: \f2c5; +$fa-var-hornbill: \f592; +$fa-var-js: \f3b8; +$fa-var-ideal: \e013; +$fa-var-git: \f1d3; +$fa-var-dev: \f6cc; +$fa-var-sketch: \f7c6; +$fa-var-yandex-international: \f414; +$fa-var-cc-amex: \f1f3; +$fa-var-uber: \f402; +$fa-var-github: \f09b; +$fa-var-php: \f457; +$fa-var-alipay: \f642; +$fa-var-youtube: \f167; +$fa-var-skyatlas: \f216; +$fa-var-firefox-browser: \e007; +$fa-var-replyd: \f3e6; +$fa-var-suse: \f7d6; +$fa-var-jenkins: \f3b6; +$fa-var-twitter: \f099; +$fa-var-rockrms: \f3e9; +$fa-var-pinterest: \f0d2; +$fa-var-buffer: \f837; +$fa-var-npm: \f3d4; +$fa-var-yammer: \f840; +$fa-var-btc: \f15a; +$fa-var-dribbble: \f17d; +$fa-var-stumbleupon-circle: \f1a3; +$fa-var-internet-explorer: \f26b; +$fa-var-stubber: \e5c7; +$fa-var-telegram: \f2c6; +$fa-var-telegram-plane: \f2c6; +$fa-var-old-republic: \f510; +$fa-var-odysee: \e5c6; +$fa-var-square-whatsapp: \f40c; +$fa-var-whatsapp-square: \f40c; +$fa-var-node-js: \f3d3; +$fa-var-edge-legacy: \e078; +$fa-var-slack: \f198; +$fa-var-slack-hash: \f198; +$fa-var-medrt: \f3c8; +$fa-var-usb: \f287; +$fa-var-tumblr: \f173; +$fa-var-vaadin: \f408; +$fa-var-quora: \f2c4; +$fa-var-reacteurope: \f75d; +$fa-var-medium: \f23a; +$fa-var-medium-m: \f23a; +$fa-var-amilia: \f36d; +$fa-var-mixcloud: \f289; +$fa-var-flipboard: \f44d; +$fa-var-viacoin: \f237; +$fa-var-critical-role: \f6c9; +$fa-var-sitrox: \e44a; +$fa-var-discourse: \f393; +$fa-var-joomla: \f1aa; +$fa-var-mastodon: \f4f6; +$fa-var-airbnb: \f834; +$fa-var-wolf-pack-battalion: \f514; +$fa-var-buy-n-large: \f8a6; +$fa-var-gulp: \f3ae; +$fa-var-creative-commons-sampling-plus: \f4f1; +$fa-var-strava: \f428; +$fa-var-ember: \f423; +$fa-var-canadian-maple-leaf: \f785; +$fa-var-teamspeak: \f4f9; +$fa-var-pushed: \f3e1; +$fa-var-wordpress-simple: \f411; +$fa-var-nutritionix: \f3d6; +$fa-var-wodu: \e088; +$fa-var-google-pay: \e079; +$fa-var-intercom: \f7af; +$fa-var-zhihu: \f63f; +$fa-var-korvue: \f42f; +$fa-var-pix: \e43a; +$fa-var-steam-symbol: \f3f6; + +$fa-icons: ( + "0": $fa-var-0, + "1": $fa-var-1, + "2": $fa-var-2, + "3": $fa-var-3, + "4": $fa-var-4, + "5": $fa-var-5, + "6": $fa-var-6, + "7": $fa-var-7, + "8": $fa-var-8, + "9": $fa-var-9, + "fill-drip": $fa-var-fill-drip, + "arrows-to-circle": $fa-var-arrows-to-circle, + "circle-chevron-right": $fa-var-circle-chevron-right, + "chevron-circle-right": $fa-var-chevron-circle-right, + "at": $fa-var-at, + "trash-can": $fa-var-trash-can, + "trash-alt": $fa-var-trash-alt, + "text-height": $fa-var-text-height, + "user-xmark": $fa-var-user-xmark, + "user-times": $fa-var-user-times, + "stethoscope": $fa-var-stethoscope, + "message": $fa-var-message, + "comment-alt": $fa-var-comment-alt, + "info": $fa-var-info, + "down-left-and-up-right-to-center": $fa-var-down-left-and-up-right-to-center, + "compress-alt": $fa-var-compress-alt, + "explosion": $fa-var-explosion, + "file-lines": $fa-var-file-lines, + "file-alt": $fa-var-file-alt, + "file-text": $fa-var-file-text, + "wave-square": $fa-var-wave-square, + "ring": $fa-var-ring, + "building-un": $fa-var-building-un, + "dice-three": $fa-var-dice-three, + "calendar-days": $fa-var-calendar-days, + "calendar-alt": $fa-var-calendar-alt, + "anchor-circle-check": $fa-var-anchor-circle-check, + "building-circle-arrow-right": $fa-var-building-circle-arrow-right, + "volleyball": $fa-var-volleyball, + "volleyball-ball": $fa-var-volleyball-ball, + "arrows-up-to-line": $fa-var-arrows-up-to-line, + "sort-down": $fa-var-sort-down, + "sort-desc": $fa-var-sort-desc, + "circle-minus": $fa-var-circle-minus, + "minus-circle": $fa-var-minus-circle, + "door-open": $fa-var-door-open, + "right-from-bracket": $fa-var-right-from-bracket, + "sign-out-alt": $fa-var-sign-out-alt, + "atom": $fa-var-atom, + "soap": $fa-var-soap, + "icons": $fa-var-icons, + "heart-music-camera-bolt": $fa-var-heart-music-camera-bolt, + "microphone-lines-slash": $fa-var-microphone-lines-slash, + "microphone-alt-slash": $fa-var-microphone-alt-slash, + "bridge-circle-check": $fa-var-bridge-circle-check, + "pump-medical": $fa-var-pump-medical, + "fingerprint": $fa-var-fingerprint, + "hand-point-right": $fa-var-hand-point-right, + "magnifying-glass-location": $fa-var-magnifying-glass-location, + "search-location": $fa-var-search-location, + "forward-step": $fa-var-forward-step, + "step-forward": $fa-var-step-forward, + "face-smile-beam": $fa-var-face-smile-beam, + "smile-beam": $fa-var-smile-beam, + "flag-checkered": $fa-var-flag-checkered, + "football": $fa-var-football, + "football-ball": $fa-var-football-ball, + "school-circle-exclamation": $fa-var-school-circle-exclamation, + "crop": $fa-var-crop, + "angles-down": $fa-var-angles-down, + "angle-double-down": $fa-var-angle-double-down, + "users-rectangle": $fa-var-users-rectangle, + "people-roof": $fa-var-people-roof, + "people-line": $fa-var-people-line, + "beer-mug-empty": $fa-var-beer-mug-empty, + "beer": $fa-var-beer, + "diagram-predecessor": $fa-var-diagram-predecessor, + "arrow-up-long": $fa-var-arrow-up-long, + "long-arrow-up": $fa-var-long-arrow-up, + "fire-flame-simple": $fa-var-fire-flame-simple, + "burn": $fa-var-burn, + "person": $fa-var-person, + "male": $fa-var-male, + "laptop": $fa-var-laptop, + "file-csv": $fa-var-file-csv, + "menorah": $fa-var-menorah, + "truck-plane": $fa-var-truck-plane, + "record-vinyl": $fa-var-record-vinyl, + "face-grin-stars": $fa-var-face-grin-stars, + "grin-stars": $fa-var-grin-stars, + "bong": $fa-var-bong, + "spaghetti-monster-flying": $fa-var-spaghetti-monster-flying, + "pastafarianism": $fa-var-pastafarianism, + "arrow-down-up-across-line": $fa-var-arrow-down-up-across-line, + "spoon": $fa-var-spoon, + "utensil-spoon": $fa-var-utensil-spoon, + "jar-wheat": $fa-var-jar-wheat, + "envelopes-bulk": $fa-var-envelopes-bulk, + "mail-bulk": $fa-var-mail-bulk, + "file-circle-exclamation": $fa-var-file-circle-exclamation, + "circle-h": $fa-var-circle-h, + "hospital-symbol": $fa-var-hospital-symbol, + "pager": $fa-var-pager, + "address-book": $fa-var-address-book, + "contact-book": $fa-var-contact-book, + "strikethrough": $fa-var-strikethrough, + "k": $fa-var-k, + "landmark-flag": $fa-var-landmark-flag, + "pencil": $fa-var-pencil, + "pencil-alt": $fa-var-pencil-alt, + "backward": $fa-var-backward, + "caret-right": $fa-var-caret-right, + "comments": $fa-var-comments, + "paste": $fa-var-paste, + "file-clipboard": $fa-var-file-clipboard, + "code-pull-request": $fa-var-code-pull-request, + "clipboard-list": $fa-var-clipboard-list, + "truck-ramp-box": $fa-var-truck-ramp-box, + "truck-loading": $fa-var-truck-loading, + "user-check": $fa-var-user-check, + "vial-virus": $fa-var-vial-virus, + "sheet-plastic": $fa-var-sheet-plastic, + "blog": $fa-var-blog, + "user-ninja": $fa-var-user-ninja, + "person-arrow-up-from-line": $fa-var-person-arrow-up-from-line, + "scroll-torah": $fa-var-scroll-torah, + "torah": $fa-var-torah, + "broom-ball": $fa-var-broom-ball, + "quidditch": $fa-var-quidditch, + "quidditch-broom-ball": $fa-var-quidditch-broom-ball, + "toggle-off": $fa-var-toggle-off, + "box-archive": $fa-var-box-archive, + "archive": $fa-var-archive, + "person-drowning": $fa-var-person-drowning, + "arrow-down-9-1": $fa-var-arrow-down-9-1, + "sort-numeric-desc": $fa-var-sort-numeric-desc, + "sort-numeric-down-alt": $fa-var-sort-numeric-down-alt, + "face-grin-tongue-squint": $fa-var-face-grin-tongue-squint, + "grin-tongue-squint": $fa-var-grin-tongue-squint, + "spray-can": $fa-var-spray-can, + "truck-monster": $fa-var-truck-monster, + "w": $fa-var-w, + "earth-africa": $fa-var-earth-africa, + "globe-africa": $fa-var-globe-africa, + "rainbow": $fa-var-rainbow, + "circle-notch": $fa-var-circle-notch, + "tablet-screen-button": $fa-var-tablet-screen-button, + "tablet-alt": $fa-var-tablet-alt, + "paw": $fa-var-paw, + "cloud": $fa-var-cloud, + "trowel-bricks": $fa-var-trowel-bricks, + "face-flushed": $fa-var-face-flushed, + "flushed": $fa-var-flushed, + "hospital-user": $fa-var-hospital-user, + "tent-arrow-left-right": $fa-var-tent-arrow-left-right, + "gavel": $fa-var-gavel, + "legal": $fa-var-legal, + "binoculars": $fa-var-binoculars, + "microphone-slash": $fa-var-microphone-slash, + "box-tissue": $fa-var-box-tissue, + "motorcycle": $fa-var-motorcycle, + "bell-concierge": $fa-var-bell-concierge, + "concierge-bell": $fa-var-concierge-bell, + "pen-ruler": $fa-var-pen-ruler, + "pencil-ruler": $fa-var-pencil-ruler, + "people-arrows": $fa-var-people-arrows, + "people-arrows-left-right": $fa-var-people-arrows-left-right, + "mars-and-venus-burst": $fa-var-mars-and-venus-burst, + "square-caret-right": $fa-var-square-caret-right, + "caret-square-right": $fa-var-caret-square-right, + "scissors": $fa-var-scissors, + "cut": $fa-var-cut, + "sun-plant-wilt": $fa-var-sun-plant-wilt, + "toilets-portable": $fa-var-toilets-portable, + "hockey-puck": $fa-var-hockey-puck, + "table": $fa-var-table, + "magnifying-glass-arrow-right": $fa-var-magnifying-glass-arrow-right, + "tachograph-digital": $fa-var-tachograph-digital, + "digital-tachograph": $fa-var-digital-tachograph, + "users-slash": $fa-var-users-slash, + "clover": $fa-var-clover, + "reply": $fa-var-reply, + "mail-reply": $fa-var-mail-reply, + "star-and-crescent": $fa-var-star-and-crescent, + "house-fire": $fa-var-house-fire, + "square-minus": $fa-var-square-minus, + "minus-square": $fa-var-minus-square, + "helicopter": $fa-var-helicopter, + "compass": $fa-var-compass, + "square-caret-down": $fa-var-square-caret-down, + "caret-square-down": $fa-var-caret-square-down, + "file-circle-question": $fa-var-file-circle-question, + "laptop-code": $fa-var-laptop-code, + "swatchbook": $fa-var-swatchbook, + "prescription-bottle": $fa-var-prescription-bottle, + "bars": $fa-var-bars, + "navicon": $fa-var-navicon, + "people-group": $fa-var-people-group, + "hourglass-end": $fa-var-hourglass-end, + "hourglass-3": $fa-var-hourglass-3, + "heart-crack": $fa-var-heart-crack, + "heart-broken": $fa-var-heart-broken, + "square-up-right": $fa-var-square-up-right, + "external-link-square-alt": $fa-var-external-link-square-alt, + "face-kiss-beam": $fa-var-face-kiss-beam, + "kiss-beam": $fa-var-kiss-beam, + "film": $fa-var-film, + "ruler-horizontal": $fa-var-ruler-horizontal, + "people-robbery": $fa-var-people-robbery, + "lightbulb": $fa-var-lightbulb, + "caret-left": $fa-var-caret-left, + "circle-exclamation": $fa-var-circle-exclamation, + "exclamation-circle": $fa-var-exclamation-circle, + "school-circle-xmark": $fa-var-school-circle-xmark, + "arrow-right-from-bracket": $fa-var-arrow-right-from-bracket, + "sign-out": $fa-var-sign-out, + "circle-chevron-down": $fa-var-circle-chevron-down, + "chevron-circle-down": $fa-var-chevron-circle-down, + "unlock-keyhole": $fa-var-unlock-keyhole, + "unlock-alt": $fa-var-unlock-alt, + "cloud-showers-heavy": $fa-var-cloud-showers-heavy, + "headphones-simple": $fa-var-headphones-simple, + "headphones-alt": $fa-var-headphones-alt, + "sitemap": $fa-var-sitemap, + "circle-dollar-to-slot": $fa-var-circle-dollar-to-slot, + "donate": $fa-var-donate, + "memory": $fa-var-memory, + "road-spikes": $fa-var-road-spikes, + "fire-burner": $fa-var-fire-burner, + "flag": $fa-var-flag, + "hanukiah": $fa-var-hanukiah, + "feather": $fa-var-feather, + "volume-low": $fa-var-volume-low, + "volume-down": $fa-var-volume-down, + "comment-slash": $fa-var-comment-slash, + "cloud-sun-rain": $fa-var-cloud-sun-rain, + "compress": $fa-var-compress, + "wheat-awn": $fa-var-wheat-awn, + "wheat-alt": $fa-var-wheat-alt, + "ankh": $fa-var-ankh, + "hands-holding-child": $fa-var-hands-holding-child, + "asterisk": $fa-var-asterisk, + "square-check": $fa-var-square-check, + "check-square": $fa-var-check-square, + "peseta-sign": $fa-var-peseta-sign, + "heading": $fa-var-heading, + "header": $fa-var-header, + "ghost": $fa-var-ghost, + "list": $fa-var-list, + "list-squares": $fa-var-list-squares, + "square-phone-flip": $fa-var-square-phone-flip, + "phone-square-alt": $fa-var-phone-square-alt, + "cart-plus": $fa-var-cart-plus, + "gamepad": $fa-var-gamepad, + "circle-dot": $fa-var-circle-dot, + "dot-circle": $fa-var-dot-circle, + "face-dizzy": $fa-var-face-dizzy, + "dizzy": $fa-var-dizzy, + "egg": $fa-var-egg, + "house-medical-circle-xmark": $fa-var-house-medical-circle-xmark, + "campground": $fa-var-campground, + "folder-plus": $fa-var-folder-plus, + "futbol": $fa-var-futbol, + "futbol-ball": $fa-var-futbol-ball, + "soccer-ball": $fa-var-soccer-ball, + "paintbrush": $fa-var-paintbrush, + "paint-brush": $fa-var-paint-brush, + "lock": $fa-var-lock, + "gas-pump": $fa-var-gas-pump, + "hot-tub-person": $fa-var-hot-tub-person, + "hot-tub": $fa-var-hot-tub, + "map-location": $fa-var-map-location, + "map-marked": $fa-var-map-marked, + "house-flood-water": $fa-var-house-flood-water, + "tree": $fa-var-tree, + "bridge-lock": $fa-var-bridge-lock, + "sack-dollar": $fa-var-sack-dollar, + "pen-to-square": $fa-var-pen-to-square, + "edit": $fa-var-edit, + "car-side": $fa-var-car-side, + "share-nodes": $fa-var-share-nodes, + "share-alt": $fa-var-share-alt, + "heart-circle-minus": $fa-var-heart-circle-minus, + "hourglass-half": $fa-var-hourglass-half, + "hourglass-2": $fa-var-hourglass-2, + "microscope": $fa-var-microscope, + "sink": $fa-var-sink, + "bag-shopping": $fa-var-bag-shopping, + "shopping-bag": $fa-var-shopping-bag, + "arrow-down-z-a": $fa-var-arrow-down-z-a, + "sort-alpha-desc": $fa-var-sort-alpha-desc, + "sort-alpha-down-alt": $fa-var-sort-alpha-down-alt, + "mitten": $fa-var-mitten, + "person-rays": $fa-var-person-rays, + "users": $fa-var-users, + "eye-slash": $fa-var-eye-slash, + "flask-vial": $fa-var-flask-vial, + "hand": $fa-var-hand, + "hand-paper": $fa-var-hand-paper, + "om": $fa-var-om, + "worm": $fa-var-worm, + "house-circle-xmark": $fa-var-house-circle-xmark, + "plug": $fa-var-plug, + "chevron-up": $fa-var-chevron-up, + "hand-spock": $fa-var-hand-spock, + "stopwatch": $fa-var-stopwatch, + "face-kiss": $fa-var-face-kiss, + "kiss": $fa-var-kiss, + "bridge-circle-xmark": $fa-var-bridge-circle-xmark, + "face-grin-tongue": $fa-var-face-grin-tongue, + "grin-tongue": $fa-var-grin-tongue, + "chess-bishop": $fa-var-chess-bishop, + "face-grin-wink": $fa-var-face-grin-wink, + "grin-wink": $fa-var-grin-wink, + "ear-deaf": $fa-var-ear-deaf, + "deaf": $fa-var-deaf, + "deafness": $fa-var-deafness, + "hard-of-hearing": $fa-var-hard-of-hearing, + "road-circle-check": $fa-var-road-circle-check, + "dice-five": $fa-var-dice-five, + "square-rss": $fa-var-square-rss, + "rss-square": $fa-var-rss-square, + "land-mine-on": $fa-var-land-mine-on, + "i-cursor": $fa-var-i-cursor, + "stamp": $fa-var-stamp, + "stairs": $fa-var-stairs, + "i": $fa-var-i, + "hryvnia-sign": $fa-var-hryvnia-sign, + "hryvnia": $fa-var-hryvnia, + "pills": $fa-var-pills, + "face-grin-wide": $fa-var-face-grin-wide, + "grin-alt": $fa-var-grin-alt, + "tooth": $fa-var-tooth, + "v": $fa-var-v, + "bangladeshi-taka-sign": $fa-var-bangladeshi-taka-sign, + "bicycle": $fa-var-bicycle, + "staff-snake": $fa-var-staff-snake, + "rod-asclepius": $fa-var-rod-asclepius, + "rod-snake": $fa-var-rod-snake, + "staff-aesculapius": $fa-var-staff-aesculapius, + "head-side-cough-slash": $fa-var-head-side-cough-slash, + "truck-medical": $fa-var-truck-medical, + "ambulance": $fa-var-ambulance, + "wheat-awn-circle-exclamation": $fa-var-wheat-awn-circle-exclamation, + "snowman": $fa-var-snowman, + "mortar-pestle": $fa-var-mortar-pestle, + "road-barrier": $fa-var-road-barrier, + "school": $fa-var-school, + "igloo": $fa-var-igloo, + "joint": $fa-var-joint, + "angle-right": $fa-var-angle-right, + "horse": $fa-var-horse, + "q": $fa-var-q, + "g": $fa-var-g, + "notes-medical": $fa-var-notes-medical, + "temperature-half": $fa-var-temperature-half, + "temperature-2": $fa-var-temperature-2, + "thermometer-2": $fa-var-thermometer-2, + "thermometer-half": $fa-var-thermometer-half, + "dong-sign": $fa-var-dong-sign, + "capsules": $fa-var-capsules, + "poo-storm": $fa-var-poo-storm, + "poo-bolt": $fa-var-poo-bolt, + "face-frown-open": $fa-var-face-frown-open, + "frown-open": $fa-var-frown-open, + "hand-point-up": $fa-var-hand-point-up, + "money-bill": $fa-var-money-bill, + "bookmark": $fa-var-bookmark, + "align-justify": $fa-var-align-justify, + "umbrella-beach": $fa-var-umbrella-beach, + "helmet-un": $fa-var-helmet-un, + "bullseye": $fa-var-bullseye, + "bacon": $fa-var-bacon, + "hand-point-down": $fa-var-hand-point-down, + "arrow-up-from-bracket": $fa-var-arrow-up-from-bracket, + "folder": $fa-var-folder, + "folder-blank": $fa-var-folder-blank, + "file-waveform": $fa-var-file-waveform, + "file-medical-alt": $fa-var-file-medical-alt, + "radiation": $fa-var-radiation, + "chart-simple": $fa-var-chart-simple, + "mars-stroke": $fa-var-mars-stroke, + "vial": $fa-var-vial, + "gauge": $fa-var-gauge, + "dashboard": $fa-var-dashboard, + "gauge-med": $fa-var-gauge-med, + "tachometer-alt-average": $fa-var-tachometer-alt-average, + "wand-magic-sparkles": $fa-var-wand-magic-sparkles, + "magic-wand-sparkles": $fa-var-magic-wand-sparkles, + "e": $fa-var-e, + "pen-clip": $fa-var-pen-clip, + "pen-alt": $fa-var-pen-alt, + "bridge-circle-exclamation": $fa-var-bridge-circle-exclamation, + "user": $fa-var-user, + "school-circle-check": $fa-var-school-circle-check, + "dumpster": $fa-var-dumpster, + "van-shuttle": $fa-var-van-shuttle, + "shuttle-van": $fa-var-shuttle-van, + "building-user": $fa-var-building-user, + "square-caret-left": $fa-var-square-caret-left, + "caret-square-left": $fa-var-caret-square-left, + "highlighter": $fa-var-highlighter, + "key": $fa-var-key, + "bullhorn": $fa-var-bullhorn, + "globe": $fa-var-globe, + "synagogue": $fa-var-synagogue, + "person-half-dress": $fa-var-person-half-dress, + "road-bridge": $fa-var-road-bridge, + "location-arrow": $fa-var-location-arrow, + "c": $fa-var-c, + "tablet-button": $fa-var-tablet-button, + "building-lock": $fa-var-building-lock, + "pizza-slice": $fa-var-pizza-slice, + "money-bill-wave": $fa-var-money-bill-wave, + "chart-area": $fa-var-chart-area, + "area-chart": $fa-var-area-chart, + "house-flag": $fa-var-house-flag, + "person-circle-minus": $fa-var-person-circle-minus, + "ban": $fa-var-ban, + "cancel": $fa-var-cancel, + "camera-rotate": $fa-var-camera-rotate, + "spray-can-sparkles": $fa-var-spray-can-sparkles, + "air-freshener": $fa-var-air-freshener, + "star": $fa-var-star, + "repeat": $fa-var-repeat, + "cross": $fa-var-cross, + "box": $fa-var-box, + "venus-mars": $fa-var-venus-mars, + "arrow-pointer": $fa-var-arrow-pointer, + "mouse-pointer": $fa-var-mouse-pointer, + "maximize": $fa-var-maximize, + "expand-arrows-alt": $fa-var-expand-arrows-alt, + "charging-station": $fa-var-charging-station, + "shapes": $fa-var-shapes, + "triangle-circle-square": $fa-var-triangle-circle-square, + "shuffle": $fa-var-shuffle, + "random": $fa-var-random, + "person-running": $fa-var-person-running, + "running": $fa-var-running, + "mobile-retro": $fa-var-mobile-retro, + "grip-lines-vertical": $fa-var-grip-lines-vertical, + "spider": $fa-var-spider, + "hands-bound": $fa-var-hands-bound, + "file-invoice-dollar": $fa-var-file-invoice-dollar, + "plane-circle-exclamation": $fa-var-plane-circle-exclamation, + "x-ray": $fa-var-x-ray, + "spell-check": $fa-var-spell-check, + "slash": $fa-var-slash, + "computer-mouse": $fa-var-computer-mouse, + "mouse": $fa-var-mouse, + "arrow-right-to-bracket": $fa-var-arrow-right-to-bracket, + "sign-in": $fa-var-sign-in, + "shop-slash": $fa-var-shop-slash, + "store-alt-slash": $fa-var-store-alt-slash, + "server": $fa-var-server, + "virus-covid-slash": $fa-var-virus-covid-slash, + "shop-lock": $fa-var-shop-lock, + "hourglass-start": $fa-var-hourglass-start, + "hourglass-1": $fa-var-hourglass-1, + "blender-phone": $fa-var-blender-phone, + "building-wheat": $fa-var-building-wheat, + "person-breastfeeding": $fa-var-person-breastfeeding, + "right-to-bracket": $fa-var-right-to-bracket, + "sign-in-alt": $fa-var-sign-in-alt, + "venus": $fa-var-venus, + "passport": $fa-var-passport, + "heart-pulse": $fa-var-heart-pulse, + "heartbeat": $fa-var-heartbeat, + "people-carry-box": $fa-var-people-carry-box, + "people-carry": $fa-var-people-carry, + "temperature-high": $fa-var-temperature-high, + "microchip": $fa-var-microchip, + "crown": $fa-var-crown, + "weight-hanging": $fa-var-weight-hanging, + "xmarks-lines": $fa-var-xmarks-lines, + "file-prescription": $fa-var-file-prescription, + "weight-scale": $fa-var-weight-scale, + "weight": $fa-var-weight, + "user-group": $fa-var-user-group, + "user-friends": $fa-var-user-friends, + "arrow-up-a-z": $fa-var-arrow-up-a-z, + "sort-alpha-up": $fa-var-sort-alpha-up, + "chess-knight": $fa-var-chess-knight, + "face-laugh-squint": $fa-var-face-laugh-squint, + "laugh-squint": $fa-var-laugh-squint, + "wheelchair": $fa-var-wheelchair, + "circle-arrow-up": $fa-var-circle-arrow-up, + "arrow-circle-up": $fa-var-arrow-circle-up, + "toggle-on": $fa-var-toggle-on, + "person-walking": $fa-var-person-walking, + "walking": $fa-var-walking, + "l": $fa-var-l, + "fire": $fa-var-fire, + "bed-pulse": $fa-var-bed-pulse, + "procedures": $fa-var-procedures, + "shuttle-space": $fa-var-shuttle-space, + "space-shuttle": $fa-var-space-shuttle, + "face-laugh": $fa-var-face-laugh, + "laugh": $fa-var-laugh, + "folder-open": $fa-var-folder-open, + "heart-circle-plus": $fa-var-heart-circle-plus, + "code-fork": $fa-var-code-fork, + "city": $fa-var-city, + "microphone-lines": $fa-var-microphone-lines, + "microphone-alt": $fa-var-microphone-alt, + "pepper-hot": $fa-var-pepper-hot, + "unlock": $fa-var-unlock, + "colon-sign": $fa-var-colon-sign, + "headset": $fa-var-headset, + "store-slash": $fa-var-store-slash, + "road-circle-xmark": $fa-var-road-circle-xmark, + "user-minus": $fa-var-user-minus, + "mars-stroke-up": $fa-var-mars-stroke-up, + "mars-stroke-v": $fa-var-mars-stroke-v, + "champagne-glasses": $fa-var-champagne-glasses, + "glass-cheers": $fa-var-glass-cheers, + "clipboard": $fa-var-clipboard, + "house-circle-exclamation": $fa-var-house-circle-exclamation, + "file-arrow-up": $fa-var-file-arrow-up, + "file-upload": $fa-var-file-upload, + "wifi": $fa-var-wifi, + "wifi-3": $fa-var-wifi-3, + "wifi-strong": $fa-var-wifi-strong, + "bath": $fa-var-bath, + "bathtub": $fa-var-bathtub, + "underline": $fa-var-underline, + "user-pen": $fa-var-user-pen, + "user-edit": $fa-var-user-edit, + "signature": $fa-var-signature, + "stroopwafel": $fa-var-stroopwafel, + "bold": $fa-var-bold, + "anchor-lock": $fa-var-anchor-lock, + "building-ngo": $fa-var-building-ngo, + "manat-sign": $fa-var-manat-sign, + "not-equal": $fa-var-not-equal, + "border-top-left": $fa-var-border-top-left, + "border-style": $fa-var-border-style, + "map-location-dot": $fa-var-map-location-dot, + "map-marked-alt": $fa-var-map-marked-alt, + "jedi": $fa-var-jedi, + "square-poll-vertical": $fa-var-square-poll-vertical, + "poll": $fa-var-poll, + "mug-hot": $fa-var-mug-hot, + "car-battery": $fa-var-car-battery, + "battery-car": $fa-var-battery-car, + "gift": $fa-var-gift, + "dice-two": $fa-var-dice-two, + "chess-queen": $fa-var-chess-queen, + "glasses": $fa-var-glasses, + "chess-board": $fa-var-chess-board, + "building-circle-check": $fa-var-building-circle-check, + "person-chalkboard": $fa-var-person-chalkboard, + "mars-stroke-right": $fa-var-mars-stroke-right, + "mars-stroke-h": $fa-var-mars-stroke-h, + "hand-back-fist": $fa-var-hand-back-fist, + "hand-rock": $fa-var-hand-rock, + "square-caret-up": $fa-var-square-caret-up, + "caret-square-up": $fa-var-caret-square-up, + "cloud-showers-water": $fa-var-cloud-showers-water, + "chart-bar": $fa-var-chart-bar, + "bar-chart": $fa-var-bar-chart, + "hands-bubbles": $fa-var-hands-bubbles, + "hands-wash": $fa-var-hands-wash, + "less-than-equal": $fa-var-less-than-equal, + "train": $fa-var-train, + "eye-low-vision": $fa-var-eye-low-vision, + "low-vision": $fa-var-low-vision, + "crow": $fa-var-crow, + "sailboat": $fa-var-sailboat, + "window-restore": $fa-var-window-restore, + "square-plus": $fa-var-square-plus, + "plus-square": $fa-var-plus-square, + "torii-gate": $fa-var-torii-gate, + "frog": $fa-var-frog, + "bucket": $fa-var-bucket, + "image": $fa-var-image, + "microphone": $fa-var-microphone, + "cow": $fa-var-cow, + "caret-up": $fa-var-caret-up, + "screwdriver": $fa-var-screwdriver, + "folder-closed": $fa-var-folder-closed, + "house-tsunami": $fa-var-house-tsunami, + "square-nfi": $fa-var-square-nfi, + "arrow-up-from-ground-water": $fa-var-arrow-up-from-ground-water, + "martini-glass": $fa-var-martini-glass, + "glass-martini-alt": $fa-var-glass-martini-alt, + "rotate-left": $fa-var-rotate-left, + "rotate-back": $fa-var-rotate-back, + "rotate-backward": $fa-var-rotate-backward, + "undo-alt": $fa-var-undo-alt, + "table-columns": $fa-var-table-columns, + "columns": $fa-var-columns, + "lemon": $fa-var-lemon, + "head-side-mask": $fa-var-head-side-mask, + "handshake": $fa-var-handshake, + "gem": $fa-var-gem, + "dolly": $fa-var-dolly, + "dolly-box": $fa-var-dolly-box, + "smoking": $fa-var-smoking, + "minimize": $fa-var-minimize, + "compress-arrows-alt": $fa-var-compress-arrows-alt, + "monument": $fa-var-monument, + "snowplow": $fa-var-snowplow, + "angles-right": $fa-var-angles-right, + "angle-double-right": $fa-var-angle-double-right, + "cannabis": $fa-var-cannabis, + "circle-play": $fa-var-circle-play, + "play-circle": $fa-var-play-circle, + "tablets": $fa-var-tablets, + "ethernet": $fa-var-ethernet, + "euro-sign": $fa-var-euro-sign, + "eur": $fa-var-eur, + "euro": $fa-var-euro, + "chair": $fa-var-chair, + "circle-check": $fa-var-circle-check, + "check-circle": $fa-var-check-circle, + "circle-stop": $fa-var-circle-stop, + "stop-circle": $fa-var-stop-circle, + "compass-drafting": $fa-var-compass-drafting, + "drafting-compass": $fa-var-drafting-compass, + "plate-wheat": $fa-var-plate-wheat, + "icicles": $fa-var-icicles, + "person-shelter": $fa-var-person-shelter, + "neuter": $fa-var-neuter, + "id-badge": $fa-var-id-badge, + "marker": $fa-var-marker, + "face-laugh-beam": $fa-var-face-laugh-beam, + "laugh-beam": $fa-var-laugh-beam, + "helicopter-symbol": $fa-var-helicopter-symbol, + "universal-access": $fa-var-universal-access, + "circle-chevron-up": $fa-var-circle-chevron-up, + "chevron-circle-up": $fa-var-chevron-circle-up, + "lari-sign": $fa-var-lari-sign, + "volcano": $fa-var-volcano, + "person-walking-dashed-line-arrow-right": $fa-var-person-walking-dashed-line-arrow-right, + "sterling-sign": $fa-var-sterling-sign, + "gbp": $fa-var-gbp, + "pound-sign": $fa-var-pound-sign, + "viruses": $fa-var-viruses, + "square-person-confined": $fa-var-square-person-confined, + "user-tie": $fa-var-user-tie, + "arrow-down-long": $fa-var-arrow-down-long, + "long-arrow-down": $fa-var-long-arrow-down, + "tent-arrow-down-to-line": $fa-var-tent-arrow-down-to-line, + "certificate": $fa-var-certificate, + "reply-all": $fa-var-reply-all, + "mail-reply-all": $fa-var-mail-reply-all, + "suitcase": $fa-var-suitcase, + "person-skating": $fa-var-person-skating, + "skating": $fa-var-skating, + "filter-circle-dollar": $fa-var-filter-circle-dollar, + "funnel-dollar": $fa-var-funnel-dollar, + "camera-retro": $fa-var-camera-retro, + "circle-arrow-down": $fa-var-circle-arrow-down, + "arrow-circle-down": $fa-var-arrow-circle-down, + "file-import": $fa-var-file-import, + "arrow-right-to-file": $fa-var-arrow-right-to-file, + "square-arrow-up-right": $fa-var-square-arrow-up-right, + "external-link-square": $fa-var-external-link-square, + "box-open": $fa-var-box-open, + "scroll": $fa-var-scroll, + "spa": $fa-var-spa, + "location-pin-lock": $fa-var-location-pin-lock, + "pause": $fa-var-pause, + "hill-avalanche": $fa-var-hill-avalanche, + "temperature-empty": $fa-var-temperature-empty, + "temperature-0": $fa-var-temperature-0, + "thermometer-0": $fa-var-thermometer-0, + "thermometer-empty": $fa-var-thermometer-empty, + "bomb": $fa-var-bomb, + "registered": $fa-var-registered, + "address-card": $fa-var-address-card, + "contact-card": $fa-var-contact-card, + "vcard": $fa-var-vcard, + "scale-unbalanced-flip": $fa-var-scale-unbalanced-flip, + "balance-scale-right": $fa-var-balance-scale-right, + "subscript": $fa-var-subscript, + "diamond-turn-right": $fa-var-diamond-turn-right, + "directions": $fa-var-directions, + "burst": $fa-var-burst, + "house-laptop": $fa-var-house-laptop, + "laptop-house": $fa-var-laptop-house, + "face-tired": $fa-var-face-tired, + "tired": $fa-var-tired, + "money-bills": $fa-var-money-bills, + "smog": $fa-var-smog, + "crutch": $fa-var-crutch, + "cloud-arrow-up": $fa-var-cloud-arrow-up, + "cloud-upload": $fa-var-cloud-upload, + "cloud-upload-alt": $fa-var-cloud-upload-alt, + "palette": $fa-var-palette, + "arrows-turn-right": $fa-var-arrows-turn-right, + "vest": $fa-var-vest, + "ferry": $fa-var-ferry, + "arrows-down-to-people": $fa-var-arrows-down-to-people, + "seedling": $fa-var-seedling, + "sprout": $fa-var-sprout, + "left-right": $fa-var-left-right, + "arrows-alt-h": $fa-var-arrows-alt-h, + "boxes-packing": $fa-var-boxes-packing, + "circle-arrow-left": $fa-var-circle-arrow-left, + "arrow-circle-left": $fa-var-arrow-circle-left, + "group-arrows-rotate": $fa-var-group-arrows-rotate, + "bowl-food": $fa-var-bowl-food, + "candy-cane": $fa-var-candy-cane, + "arrow-down-wide-short": $fa-var-arrow-down-wide-short, + "sort-amount-asc": $fa-var-sort-amount-asc, + "sort-amount-down": $fa-var-sort-amount-down, + "cloud-bolt": $fa-var-cloud-bolt, + "thunderstorm": $fa-var-thunderstorm, + "text-slash": $fa-var-text-slash, + "remove-format": $fa-var-remove-format, + "face-smile-wink": $fa-var-face-smile-wink, + "smile-wink": $fa-var-smile-wink, + "file-word": $fa-var-file-word, + "file-powerpoint": $fa-var-file-powerpoint, + "arrows-left-right": $fa-var-arrows-left-right, + "arrows-h": $fa-var-arrows-h, + "house-lock": $fa-var-house-lock, + "cloud-arrow-down": $fa-var-cloud-arrow-down, + "cloud-download": $fa-var-cloud-download, + "cloud-download-alt": $fa-var-cloud-download-alt, + "children": $fa-var-children, + "chalkboard": $fa-var-chalkboard, + "blackboard": $fa-var-blackboard, + "user-large-slash": $fa-var-user-large-slash, + "user-alt-slash": $fa-var-user-alt-slash, + "envelope-open": $fa-var-envelope-open, + "handshake-simple-slash": $fa-var-handshake-simple-slash, + "handshake-alt-slash": $fa-var-handshake-alt-slash, + "mattress-pillow": $fa-var-mattress-pillow, + "guarani-sign": $fa-var-guarani-sign, + "arrows-rotate": $fa-var-arrows-rotate, + "refresh": $fa-var-refresh, + "sync": $fa-var-sync, + "fire-extinguisher": $fa-var-fire-extinguisher, + "cruzeiro-sign": $fa-var-cruzeiro-sign, + "greater-than-equal": $fa-var-greater-than-equal, + "shield-halved": $fa-var-shield-halved, + "shield-alt": $fa-var-shield-alt, + "book-atlas": $fa-var-book-atlas, + "atlas": $fa-var-atlas, + "virus": $fa-var-virus, + "envelope-circle-check": $fa-var-envelope-circle-check, + "layer-group": $fa-var-layer-group, + "arrows-to-dot": $fa-var-arrows-to-dot, + "archway": $fa-var-archway, + "heart-circle-check": $fa-var-heart-circle-check, + "house-chimney-crack": $fa-var-house-chimney-crack, + "house-damage": $fa-var-house-damage, + "file-zipper": $fa-var-file-zipper, + "file-archive": $fa-var-file-archive, + "square": $fa-var-square, + "martini-glass-empty": $fa-var-martini-glass-empty, + "glass-martini": $fa-var-glass-martini, + "couch": $fa-var-couch, + "cedi-sign": $fa-var-cedi-sign, + "italic": $fa-var-italic, + "church": $fa-var-church, + "comments-dollar": $fa-var-comments-dollar, + "democrat": $fa-var-democrat, + "z": $fa-var-z, + "person-skiing": $fa-var-person-skiing, + "skiing": $fa-var-skiing, + "road-lock": $fa-var-road-lock, + "a": $fa-var-a, + "temperature-arrow-down": $fa-var-temperature-arrow-down, + "temperature-down": $fa-var-temperature-down, + "feather-pointed": $fa-var-feather-pointed, + "feather-alt": $fa-var-feather-alt, + "p": $fa-var-p, + "snowflake": $fa-var-snowflake, + "newspaper": $fa-var-newspaper, + "rectangle-ad": $fa-var-rectangle-ad, + "ad": $fa-var-ad, + "circle-arrow-right": $fa-var-circle-arrow-right, + "arrow-circle-right": $fa-var-arrow-circle-right, + "filter-circle-xmark": $fa-var-filter-circle-xmark, + "locust": $fa-var-locust, + "sort": $fa-var-sort, + "unsorted": $fa-var-unsorted, + "list-ol": $fa-var-list-ol, + "list-1-2": $fa-var-list-1-2, + "list-numeric": $fa-var-list-numeric, + "person-dress-burst": $fa-var-person-dress-burst, + "money-check-dollar": $fa-var-money-check-dollar, + "money-check-alt": $fa-var-money-check-alt, + "vector-square": $fa-var-vector-square, + "bread-slice": $fa-var-bread-slice, + "language": $fa-var-language, + "face-kiss-wink-heart": $fa-var-face-kiss-wink-heart, + "kiss-wink-heart": $fa-var-kiss-wink-heart, + "filter": $fa-var-filter, + "question": $fa-var-question, + "file-signature": $fa-var-file-signature, + "up-down-left-right": $fa-var-up-down-left-right, + "arrows-alt": $fa-var-arrows-alt, + "house-chimney-user": $fa-var-house-chimney-user, + "hand-holding-heart": $fa-var-hand-holding-heart, + "puzzle-piece": $fa-var-puzzle-piece, + "money-check": $fa-var-money-check, + "star-half-stroke": $fa-var-star-half-stroke, + "star-half-alt": $fa-var-star-half-alt, + "code": $fa-var-code, + "whiskey-glass": $fa-var-whiskey-glass, + "glass-whiskey": $fa-var-glass-whiskey, + "building-circle-exclamation": $fa-var-building-circle-exclamation, + "magnifying-glass-chart": $fa-var-magnifying-glass-chart, + "arrow-up-right-from-square": $fa-var-arrow-up-right-from-square, + "external-link": $fa-var-external-link, + "cubes-stacked": $fa-var-cubes-stacked, + "won-sign": $fa-var-won-sign, + "krw": $fa-var-krw, + "won": $fa-var-won, + "virus-covid": $fa-var-virus-covid, + "austral-sign": $fa-var-austral-sign, + "f": $fa-var-f, + "leaf": $fa-var-leaf, + "road": $fa-var-road, + "taxi": $fa-var-taxi, + "cab": $fa-var-cab, + "person-circle-plus": $fa-var-person-circle-plus, + "chart-pie": $fa-var-chart-pie, + "pie-chart": $fa-var-pie-chart, + "bolt-lightning": $fa-var-bolt-lightning, + "sack-xmark": $fa-var-sack-xmark, + "file-excel": $fa-var-file-excel, + "file-contract": $fa-var-file-contract, + "fish-fins": $fa-var-fish-fins, + "building-flag": $fa-var-building-flag, + "face-grin-beam": $fa-var-face-grin-beam, + "grin-beam": $fa-var-grin-beam, + "object-ungroup": $fa-var-object-ungroup, + "poop": $fa-var-poop, + "location-pin": $fa-var-location-pin, + "map-marker": $fa-var-map-marker, + "kaaba": $fa-var-kaaba, + "toilet-paper": $fa-var-toilet-paper, + "helmet-safety": $fa-var-helmet-safety, + "hard-hat": $fa-var-hard-hat, + "hat-hard": $fa-var-hat-hard, + "eject": $fa-var-eject, + "circle-right": $fa-var-circle-right, + "arrow-alt-circle-right": $fa-var-arrow-alt-circle-right, + "plane-circle-check": $fa-var-plane-circle-check, + "face-rolling-eyes": $fa-var-face-rolling-eyes, + "meh-rolling-eyes": $fa-var-meh-rolling-eyes, + "object-group": $fa-var-object-group, + "chart-line": $fa-var-chart-line, + "line-chart": $fa-var-line-chart, + "mask-ventilator": $fa-var-mask-ventilator, + "arrow-right": $fa-var-arrow-right, + "signs-post": $fa-var-signs-post, + "map-signs": $fa-var-map-signs, + "cash-register": $fa-var-cash-register, + "person-circle-question": $fa-var-person-circle-question, + "h": $fa-var-h, + "tarp": $fa-var-tarp, + "screwdriver-wrench": $fa-var-screwdriver-wrench, + "tools": $fa-var-tools, + "arrows-to-eye": $fa-var-arrows-to-eye, + "plug-circle-bolt": $fa-var-plug-circle-bolt, + "heart": $fa-var-heart, + "mars-and-venus": $fa-var-mars-and-venus, + "house-user": $fa-var-house-user, + "home-user": $fa-var-home-user, + "dumpster-fire": $fa-var-dumpster-fire, + "house-crack": $fa-var-house-crack, + "martini-glass-citrus": $fa-var-martini-glass-citrus, + "cocktail": $fa-var-cocktail, + "face-surprise": $fa-var-face-surprise, + "surprise": $fa-var-surprise, + "bottle-water": $fa-var-bottle-water, + "circle-pause": $fa-var-circle-pause, + "pause-circle": $fa-var-pause-circle, + "toilet-paper-slash": $fa-var-toilet-paper-slash, + "apple-whole": $fa-var-apple-whole, + "apple-alt": $fa-var-apple-alt, + "kitchen-set": $fa-var-kitchen-set, + "r": $fa-var-r, + "temperature-quarter": $fa-var-temperature-quarter, + "temperature-1": $fa-var-temperature-1, + "thermometer-1": $fa-var-thermometer-1, + "thermometer-quarter": $fa-var-thermometer-quarter, + "cube": $fa-var-cube, + "bitcoin-sign": $fa-var-bitcoin-sign, + "shield-dog": $fa-var-shield-dog, + "solar-panel": $fa-var-solar-panel, + "lock-open": $fa-var-lock-open, + "elevator": $fa-var-elevator, + "money-bill-transfer": $fa-var-money-bill-transfer, + "money-bill-trend-up": $fa-var-money-bill-trend-up, + "house-flood-water-circle-arrow-right": $fa-var-house-flood-water-circle-arrow-right, + "square-poll-horizontal": $fa-var-square-poll-horizontal, + "poll-h": $fa-var-poll-h, + "circle": $fa-var-circle, + "backward-fast": $fa-var-backward-fast, + "fast-backward": $fa-var-fast-backward, + "recycle": $fa-var-recycle, + "user-astronaut": $fa-var-user-astronaut, + "plane-slash": $fa-var-plane-slash, + "trademark": $fa-var-trademark, + "basketball": $fa-var-basketball, + "basketball-ball": $fa-var-basketball-ball, + "satellite-dish": $fa-var-satellite-dish, + "circle-up": $fa-var-circle-up, + "arrow-alt-circle-up": $fa-var-arrow-alt-circle-up, + "mobile-screen-button": $fa-var-mobile-screen-button, + "mobile-alt": $fa-var-mobile-alt, + "volume-high": $fa-var-volume-high, + "volume-up": $fa-var-volume-up, + "users-rays": $fa-var-users-rays, + "wallet": $fa-var-wallet, + "clipboard-check": $fa-var-clipboard-check, + "file-audio": $fa-var-file-audio, + "burger": $fa-var-burger, + "hamburger": $fa-var-hamburger, + "wrench": $fa-var-wrench, + "bugs": $fa-var-bugs, + "rupee-sign": $fa-var-rupee-sign, + "rupee": $fa-var-rupee, + "file-image": $fa-var-file-image, + "circle-question": $fa-var-circle-question, + "question-circle": $fa-var-question-circle, + "plane-departure": $fa-var-plane-departure, + "handshake-slash": $fa-var-handshake-slash, + "book-bookmark": $fa-var-book-bookmark, + "code-branch": $fa-var-code-branch, + "hat-cowboy": $fa-var-hat-cowboy, + "bridge": $fa-var-bridge, + "phone-flip": $fa-var-phone-flip, + "phone-alt": $fa-var-phone-alt, + "truck-front": $fa-var-truck-front, + "cat": $fa-var-cat, + "anchor-circle-exclamation": $fa-var-anchor-circle-exclamation, + "truck-field": $fa-var-truck-field, + "route": $fa-var-route, + "clipboard-question": $fa-var-clipboard-question, + "panorama": $fa-var-panorama, + "comment-medical": $fa-var-comment-medical, + "teeth-open": $fa-var-teeth-open, + "file-circle-minus": $fa-var-file-circle-minus, + "tags": $fa-var-tags, + "wine-glass": $fa-var-wine-glass, + "forward-fast": $fa-var-forward-fast, + "fast-forward": $fa-var-fast-forward, + "face-meh-blank": $fa-var-face-meh-blank, + "meh-blank": $fa-var-meh-blank, + "square-parking": $fa-var-square-parking, + "parking": $fa-var-parking, + "house-signal": $fa-var-house-signal, + "bars-progress": $fa-var-bars-progress, + "tasks-alt": $fa-var-tasks-alt, + "faucet-drip": $fa-var-faucet-drip, + "cart-flatbed": $fa-var-cart-flatbed, + "dolly-flatbed": $fa-var-dolly-flatbed, + "ban-smoking": $fa-var-ban-smoking, + "smoking-ban": $fa-var-smoking-ban, + "terminal": $fa-var-terminal, + "mobile-button": $fa-var-mobile-button, + "house-medical-flag": $fa-var-house-medical-flag, + "basket-shopping": $fa-var-basket-shopping, + "shopping-basket": $fa-var-shopping-basket, + "tape": $fa-var-tape, + "bus-simple": $fa-var-bus-simple, + "bus-alt": $fa-var-bus-alt, + "eye": $fa-var-eye, + "face-sad-cry": $fa-var-face-sad-cry, + "sad-cry": $fa-var-sad-cry, + "audio-description": $fa-var-audio-description, + "person-military-to-person": $fa-var-person-military-to-person, + "file-shield": $fa-var-file-shield, + "user-slash": $fa-var-user-slash, + "pen": $fa-var-pen, + "tower-observation": $fa-var-tower-observation, + "file-code": $fa-var-file-code, + "signal": $fa-var-signal, + "signal-5": $fa-var-signal-5, + "signal-perfect": $fa-var-signal-perfect, + "bus": $fa-var-bus, + "heart-circle-xmark": $fa-var-heart-circle-xmark, + "house-chimney": $fa-var-house-chimney, + "home-lg": $fa-var-home-lg, + "window-maximize": $fa-var-window-maximize, + "face-frown": $fa-var-face-frown, + "frown": $fa-var-frown, + "prescription": $fa-var-prescription, + "shop": $fa-var-shop, + "store-alt": $fa-var-store-alt, + "floppy-disk": $fa-var-floppy-disk, + "save": $fa-var-save, + "vihara": $fa-var-vihara, + "scale-unbalanced": $fa-var-scale-unbalanced, + "balance-scale-left": $fa-var-balance-scale-left, + "sort-up": $fa-var-sort-up, + "sort-asc": $fa-var-sort-asc, + "comment-dots": $fa-var-comment-dots, + "commenting": $fa-var-commenting, + "plant-wilt": $fa-var-plant-wilt, + "diamond": $fa-var-diamond, + "face-grin-squint": $fa-var-face-grin-squint, + "grin-squint": $fa-var-grin-squint, + "hand-holding-dollar": $fa-var-hand-holding-dollar, + "hand-holding-usd": $fa-var-hand-holding-usd, + "bacterium": $fa-var-bacterium, + "hand-pointer": $fa-var-hand-pointer, + "drum-steelpan": $fa-var-drum-steelpan, + "hand-scissors": $fa-var-hand-scissors, + "hands-praying": $fa-var-hands-praying, + "praying-hands": $fa-var-praying-hands, + "arrow-rotate-right": $fa-var-arrow-rotate-right, + "arrow-right-rotate": $fa-var-arrow-right-rotate, + "arrow-rotate-forward": $fa-var-arrow-rotate-forward, + "redo": $fa-var-redo, + "biohazard": $fa-var-biohazard, + "location-crosshairs": $fa-var-location-crosshairs, + "location": $fa-var-location, + "mars-double": $fa-var-mars-double, + "child-dress": $fa-var-child-dress, + "users-between-lines": $fa-var-users-between-lines, + "lungs-virus": $fa-var-lungs-virus, + "face-grin-tears": $fa-var-face-grin-tears, + "grin-tears": $fa-var-grin-tears, + "phone": $fa-var-phone, + "calendar-xmark": $fa-var-calendar-xmark, + "calendar-times": $fa-var-calendar-times, + "child-reaching": $fa-var-child-reaching, + "head-side-virus": $fa-var-head-side-virus, + "user-gear": $fa-var-user-gear, + "user-cog": $fa-var-user-cog, + "arrow-up-1-9": $fa-var-arrow-up-1-9, + "sort-numeric-up": $fa-var-sort-numeric-up, + "door-closed": $fa-var-door-closed, + "shield-virus": $fa-var-shield-virus, + "dice-six": $fa-var-dice-six, + "mosquito-net": $fa-var-mosquito-net, + "bridge-water": $fa-var-bridge-water, + "person-booth": $fa-var-person-booth, + "text-width": $fa-var-text-width, + "hat-wizard": $fa-var-hat-wizard, + "pen-fancy": $fa-var-pen-fancy, + "person-digging": $fa-var-person-digging, + "digging": $fa-var-digging, + "trash": $fa-var-trash, + "gauge-simple": $fa-var-gauge-simple, + "gauge-simple-med": $fa-var-gauge-simple-med, + "tachometer-average": $fa-var-tachometer-average, + "book-medical": $fa-var-book-medical, + "poo": $fa-var-poo, + "quote-right": $fa-var-quote-right, + "quote-right-alt": $fa-var-quote-right-alt, + "shirt": $fa-var-shirt, + "t-shirt": $fa-var-t-shirt, + "tshirt": $fa-var-tshirt, + "cubes": $fa-var-cubes, + "divide": $fa-var-divide, + "tenge-sign": $fa-var-tenge-sign, + "tenge": $fa-var-tenge, + "headphones": $fa-var-headphones, + "hands-holding": $fa-var-hands-holding, + "hands-clapping": $fa-var-hands-clapping, + "republican": $fa-var-republican, + "arrow-left": $fa-var-arrow-left, + "person-circle-xmark": $fa-var-person-circle-xmark, + "ruler": $fa-var-ruler, + "align-left": $fa-var-align-left, + "dice-d6": $fa-var-dice-d6, + "restroom": $fa-var-restroom, + "j": $fa-var-j, + "users-viewfinder": $fa-var-users-viewfinder, + "file-video": $fa-var-file-video, + "up-right-from-square": $fa-var-up-right-from-square, + "external-link-alt": $fa-var-external-link-alt, + "table-cells": $fa-var-table-cells, + "th": $fa-var-th, + "file-pdf": $fa-var-file-pdf, + "book-bible": $fa-var-book-bible, + "bible": $fa-var-bible, + "o": $fa-var-o, + "suitcase-medical": $fa-var-suitcase-medical, + "medkit": $fa-var-medkit, + "user-secret": $fa-var-user-secret, + "otter": $fa-var-otter, + "person-dress": $fa-var-person-dress, + "female": $fa-var-female, + "comment-dollar": $fa-var-comment-dollar, + "business-time": $fa-var-business-time, + "briefcase-clock": $fa-var-briefcase-clock, + "table-cells-large": $fa-var-table-cells-large, + "th-large": $fa-var-th-large, + "book-tanakh": $fa-var-book-tanakh, + "tanakh": $fa-var-tanakh, + "phone-volume": $fa-var-phone-volume, + "volume-control-phone": $fa-var-volume-control-phone, + "hat-cowboy-side": $fa-var-hat-cowboy-side, + "clipboard-user": $fa-var-clipboard-user, + "child": $fa-var-child, + "lira-sign": $fa-var-lira-sign, + "satellite": $fa-var-satellite, + "plane-lock": $fa-var-plane-lock, + "tag": $fa-var-tag, + "comment": $fa-var-comment, + "cake-candles": $fa-var-cake-candles, + "birthday-cake": $fa-var-birthday-cake, + "cake": $fa-var-cake, + "envelope": $fa-var-envelope, + "angles-up": $fa-var-angles-up, + "angle-double-up": $fa-var-angle-double-up, + "paperclip": $fa-var-paperclip, + "arrow-right-to-city": $fa-var-arrow-right-to-city, + "ribbon": $fa-var-ribbon, + "lungs": $fa-var-lungs, + "arrow-up-9-1": $fa-var-arrow-up-9-1, + "sort-numeric-up-alt": $fa-var-sort-numeric-up-alt, + "litecoin-sign": $fa-var-litecoin-sign, + "border-none": $fa-var-border-none, + "circle-nodes": $fa-var-circle-nodes, + "parachute-box": $fa-var-parachute-box, + "indent": $fa-var-indent, + "truck-field-un": $fa-var-truck-field-un, + "hourglass": $fa-var-hourglass, + "hourglass-empty": $fa-var-hourglass-empty, + "mountain": $fa-var-mountain, + "user-doctor": $fa-var-user-doctor, + "user-md": $fa-var-user-md, + "circle-info": $fa-var-circle-info, + "info-circle": $fa-var-info-circle, + "cloud-meatball": $fa-var-cloud-meatball, + "camera": $fa-var-camera, + "camera-alt": $fa-var-camera-alt, + "square-virus": $fa-var-square-virus, + "meteor": $fa-var-meteor, + "car-on": $fa-var-car-on, + "sleigh": $fa-var-sleigh, + "arrow-down-1-9": $fa-var-arrow-down-1-9, + "sort-numeric-asc": $fa-var-sort-numeric-asc, + "sort-numeric-down": $fa-var-sort-numeric-down, + "hand-holding-droplet": $fa-var-hand-holding-droplet, + "hand-holding-water": $fa-var-hand-holding-water, + "water": $fa-var-water, + "calendar-check": $fa-var-calendar-check, + "braille": $fa-var-braille, + "prescription-bottle-medical": $fa-var-prescription-bottle-medical, + "prescription-bottle-alt": $fa-var-prescription-bottle-alt, + "landmark": $fa-var-landmark, + "truck": $fa-var-truck, + "crosshairs": $fa-var-crosshairs, + "person-cane": $fa-var-person-cane, + "tent": $fa-var-tent, + "vest-patches": $fa-var-vest-patches, + "check-double": $fa-var-check-double, + "arrow-down-a-z": $fa-var-arrow-down-a-z, + "sort-alpha-asc": $fa-var-sort-alpha-asc, + "sort-alpha-down": $fa-var-sort-alpha-down, + "money-bill-wheat": $fa-var-money-bill-wheat, + "cookie": $fa-var-cookie, + "arrow-rotate-left": $fa-var-arrow-rotate-left, + "arrow-left-rotate": $fa-var-arrow-left-rotate, + "arrow-rotate-back": $fa-var-arrow-rotate-back, + "arrow-rotate-backward": $fa-var-arrow-rotate-backward, + "undo": $fa-var-undo, + "hard-drive": $fa-var-hard-drive, + "hdd": $fa-var-hdd, + "face-grin-squint-tears": $fa-var-face-grin-squint-tears, + "grin-squint-tears": $fa-var-grin-squint-tears, + "dumbbell": $fa-var-dumbbell, + "rectangle-list": $fa-var-rectangle-list, + "list-alt": $fa-var-list-alt, + "tarp-droplet": $fa-var-tarp-droplet, + "house-medical-circle-check": $fa-var-house-medical-circle-check, + "person-skiing-nordic": $fa-var-person-skiing-nordic, + "skiing-nordic": $fa-var-skiing-nordic, + "calendar-plus": $fa-var-calendar-plus, + "plane-arrival": $fa-var-plane-arrival, + "circle-left": $fa-var-circle-left, + "arrow-alt-circle-left": $fa-var-arrow-alt-circle-left, + "train-subway": $fa-var-train-subway, + "subway": $fa-var-subway, + "chart-gantt": $fa-var-chart-gantt, + "indian-rupee-sign": $fa-var-indian-rupee-sign, + "indian-rupee": $fa-var-indian-rupee, + "inr": $fa-var-inr, + "crop-simple": $fa-var-crop-simple, + "crop-alt": $fa-var-crop-alt, + "money-bill-1": $fa-var-money-bill-1, + "money-bill-alt": $fa-var-money-bill-alt, + "left-long": $fa-var-left-long, + "long-arrow-alt-left": $fa-var-long-arrow-alt-left, + "dna": $fa-var-dna, + "virus-slash": $fa-var-virus-slash, + "minus": $fa-var-minus, + "subtract": $fa-var-subtract, + "chess": $fa-var-chess, + "arrow-left-long": $fa-var-arrow-left-long, + "long-arrow-left": $fa-var-long-arrow-left, + "plug-circle-check": $fa-var-plug-circle-check, + "street-view": $fa-var-street-view, + "franc-sign": $fa-var-franc-sign, + "volume-off": $fa-var-volume-off, + "hands-asl-interpreting": $fa-var-hands-asl-interpreting, + "american-sign-language-interpreting": $fa-var-american-sign-language-interpreting, + "asl-interpreting": $fa-var-asl-interpreting, + "hands-american-sign-language-interpreting": $fa-var-hands-american-sign-language-interpreting, + "gear": $fa-var-gear, + "cog": $fa-var-cog, + "droplet-slash": $fa-var-droplet-slash, + "tint-slash": $fa-var-tint-slash, + "mosque": $fa-var-mosque, + "mosquito": $fa-var-mosquito, + "star-of-david": $fa-var-star-of-david, + "person-military-rifle": $fa-var-person-military-rifle, + "cart-shopping": $fa-var-cart-shopping, + "shopping-cart": $fa-var-shopping-cart, + "vials": $fa-var-vials, + "plug-circle-plus": $fa-var-plug-circle-plus, + "place-of-worship": $fa-var-place-of-worship, + "grip-vertical": $fa-var-grip-vertical, + "arrow-turn-up": $fa-var-arrow-turn-up, + "level-up": $fa-var-level-up, + "u": $fa-var-u, + "square-root-variable": $fa-var-square-root-variable, + "square-root-alt": $fa-var-square-root-alt, + "clock": $fa-var-clock, + "clock-four": $fa-var-clock-four, + "backward-step": $fa-var-backward-step, + "step-backward": $fa-var-step-backward, + "pallet": $fa-var-pallet, + "faucet": $fa-var-faucet, + "baseball-bat-ball": $fa-var-baseball-bat-ball, + "s": $fa-var-s, + "timeline": $fa-var-timeline, + "keyboard": $fa-var-keyboard, + "caret-down": $fa-var-caret-down, + "house-chimney-medical": $fa-var-house-chimney-medical, + "clinic-medical": $fa-var-clinic-medical, + "temperature-three-quarters": $fa-var-temperature-three-quarters, + "temperature-3": $fa-var-temperature-3, + "thermometer-3": $fa-var-thermometer-3, + "thermometer-three-quarters": $fa-var-thermometer-three-quarters, + "mobile-screen": $fa-var-mobile-screen, + "mobile-android-alt": $fa-var-mobile-android-alt, + "plane-up": $fa-var-plane-up, + "piggy-bank": $fa-var-piggy-bank, + "battery-half": $fa-var-battery-half, + "battery-3": $fa-var-battery-3, + "mountain-city": $fa-var-mountain-city, + "coins": $fa-var-coins, + "khanda": $fa-var-khanda, + "sliders": $fa-var-sliders, + "sliders-h": $fa-var-sliders-h, + "folder-tree": $fa-var-folder-tree, + "network-wired": $fa-var-network-wired, + "map-pin": $fa-var-map-pin, + "hamsa": $fa-var-hamsa, + "cent-sign": $fa-var-cent-sign, + "flask": $fa-var-flask, + "person-pregnant": $fa-var-person-pregnant, + "wand-sparkles": $fa-var-wand-sparkles, + "ellipsis-vertical": $fa-var-ellipsis-vertical, + "ellipsis-v": $fa-var-ellipsis-v, + "ticket": $fa-var-ticket, + "power-off": $fa-var-power-off, + "right-long": $fa-var-right-long, + "long-arrow-alt-right": $fa-var-long-arrow-alt-right, + "flag-usa": $fa-var-flag-usa, + "laptop-file": $fa-var-laptop-file, + "tty": $fa-var-tty, + "teletype": $fa-var-teletype, + "diagram-next": $fa-var-diagram-next, + "person-rifle": $fa-var-person-rifle, + "house-medical-circle-exclamation": $fa-var-house-medical-circle-exclamation, + "closed-captioning": $fa-var-closed-captioning, + "person-hiking": $fa-var-person-hiking, + "hiking": $fa-var-hiking, + "venus-double": $fa-var-venus-double, + "images": $fa-var-images, + "calculator": $fa-var-calculator, + "people-pulling": $fa-var-people-pulling, + "n": $fa-var-n, + "cable-car": $fa-var-cable-car, + "tram": $fa-var-tram, + "cloud-rain": $fa-var-cloud-rain, + "building-circle-xmark": $fa-var-building-circle-xmark, + "ship": $fa-var-ship, + "arrows-down-to-line": $fa-var-arrows-down-to-line, + "download": $fa-var-download, + "face-grin": $fa-var-face-grin, + "grin": $fa-var-grin, + "delete-left": $fa-var-delete-left, + "backspace": $fa-var-backspace, + "eye-dropper": $fa-var-eye-dropper, + "eye-dropper-empty": $fa-var-eye-dropper-empty, + "eyedropper": $fa-var-eyedropper, + "file-circle-check": $fa-var-file-circle-check, + "forward": $fa-var-forward, + "mobile": $fa-var-mobile, + "mobile-android": $fa-var-mobile-android, + "mobile-phone": $fa-var-mobile-phone, + "face-meh": $fa-var-face-meh, + "meh": $fa-var-meh, + "align-center": $fa-var-align-center, + "book-skull": $fa-var-book-skull, + "book-dead": $fa-var-book-dead, + "id-card": $fa-var-id-card, + "drivers-license": $fa-var-drivers-license, + "outdent": $fa-var-outdent, + "dedent": $fa-var-dedent, + "heart-circle-exclamation": $fa-var-heart-circle-exclamation, + "house": $fa-var-house, + "home": $fa-var-home, + "home-alt": $fa-var-home-alt, + "home-lg-alt": $fa-var-home-lg-alt, + "calendar-week": $fa-var-calendar-week, + "laptop-medical": $fa-var-laptop-medical, + "b": $fa-var-b, + "file-medical": $fa-var-file-medical, + "dice-one": $fa-var-dice-one, + "kiwi-bird": $fa-var-kiwi-bird, + "arrow-right-arrow-left": $fa-var-arrow-right-arrow-left, + "exchange": $fa-var-exchange, + "rotate-right": $fa-var-rotate-right, + "redo-alt": $fa-var-redo-alt, + "rotate-forward": $fa-var-rotate-forward, + "utensils": $fa-var-utensils, + "cutlery": $fa-var-cutlery, + "arrow-up-wide-short": $fa-var-arrow-up-wide-short, + "sort-amount-up": $fa-var-sort-amount-up, + "mill-sign": $fa-var-mill-sign, + "bowl-rice": $fa-var-bowl-rice, + "skull": $fa-var-skull, + "tower-broadcast": $fa-var-tower-broadcast, + "broadcast-tower": $fa-var-broadcast-tower, + "truck-pickup": $fa-var-truck-pickup, + "up-long": $fa-var-up-long, + "long-arrow-alt-up": $fa-var-long-arrow-alt-up, + "stop": $fa-var-stop, + "code-merge": $fa-var-code-merge, + "upload": $fa-var-upload, + "hurricane": $fa-var-hurricane, + "mound": $fa-var-mound, + "toilet-portable": $fa-var-toilet-portable, + "compact-disc": $fa-var-compact-disc, + "file-arrow-down": $fa-var-file-arrow-down, + "file-download": $fa-var-file-download, + "caravan": $fa-var-caravan, + "shield-cat": $fa-var-shield-cat, + "bolt": $fa-var-bolt, + "zap": $fa-var-zap, + "glass-water": $fa-var-glass-water, + "oil-well": $fa-var-oil-well, + "vault": $fa-var-vault, + "mars": $fa-var-mars, + "toilet": $fa-var-toilet, + "plane-circle-xmark": $fa-var-plane-circle-xmark, + "yen-sign": $fa-var-yen-sign, + "cny": $fa-var-cny, + "jpy": $fa-var-jpy, + "rmb": $fa-var-rmb, + "yen": $fa-var-yen, + "ruble-sign": $fa-var-ruble-sign, + "rouble": $fa-var-rouble, + "rub": $fa-var-rub, + "ruble": $fa-var-ruble, + "sun": $fa-var-sun, + "guitar": $fa-var-guitar, + "face-laugh-wink": $fa-var-face-laugh-wink, + "laugh-wink": $fa-var-laugh-wink, + "horse-head": $fa-var-horse-head, + "bore-hole": $fa-var-bore-hole, + "industry": $fa-var-industry, + "circle-down": $fa-var-circle-down, + "arrow-alt-circle-down": $fa-var-arrow-alt-circle-down, + "arrows-turn-to-dots": $fa-var-arrows-turn-to-dots, + "florin-sign": $fa-var-florin-sign, + "arrow-down-short-wide": $fa-var-arrow-down-short-wide, + "sort-amount-desc": $fa-var-sort-amount-desc, + "sort-amount-down-alt": $fa-var-sort-amount-down-alt, + "less-than": $fa-var-less-than, + "angle-down": $fa-var-angle-down, + "car-tunnel": $fa-var-car-tunnel, + "head-side-cough": $fa-var-head-side-cough, + "grip-lines": $fa-var-grip-lines, + "thumbs-down": $fa-var-thumbs-down, + "user-lock": $fa-var-user-lock, + "arrow-right-long": $fa-var-arrow-right-long, + "long-arrow-right": $fa-var-long-arrow-right, + "anchor-circle-xmark": $fa-var-anchor-circle-xmark, + "ellipsis": $fa-var-ellipsis, + "ellipsis-h": $fa-var-ellipsis-h, + "chess-pawn": $fa-var-chess-pawn, + "kit-medical": $fa-var-kit-medical, + "first-aid": $fa-var-first-aid, + "person-through-window": $fa-var-person-through-window, + "toolbox": $fa-var-toolbox, + "hands-holding-circle": $fa-var-hands-holding-circle, + "bug": $fa-var-bug, + "credit-card": $fa-var-credit-card, + "credit-card-alt": $fa-var-credit-card-alt, + "car": $fa-var-car, + "automobile": $fa-var-automobile, + "hand-holding-hand": $fa-var-hand-holding-hand, + "book-open-reader": $fa-var-book-open-reader, + "book-reader": $fa-var-book-reader, + "mountain-sun": $fa-var-mountain-sun, + "arrows-left-right-to-line": $fa-var-arrows-left-right-to-line, + "dice-d20": $fa-var-dice-d20, + "truck-droplet": $fa-var-truck-droplet, + "file-circle-xmark": $fa-var-file-circle-xmark, + "temperature-arrow-up": $fa-var-temperature-arrow-up, + "temperature-up": $fa-var-temperature-up, + "medal": $fa-var-medal, + "bed": $fa-var-bed, + "square-h": $fa-var-square-h, + "h-square": $fa-var-h-square, + "podcast": $fa-var-podcast, + "temperature-full": $fa-var-temperature-full, + "temperature-4": $fa-var-temperature-4, + "thermometer-4": $fa-var-thermometer-4, + "thermometer-full": $fa-var-thermometer-full, + "bell": $fa-var-bell, + "superscript": $fa-var-superscript, + "plug-circle-xmark": $fa-var-plug-circle-xmark, + "star-of-life": $fa-var-star-of-life, + "phone-slash": $fa-var-phone-slash, + "paint-roller": $fa-var-paint-roller, + "handshake-angle": $fa-var-handshake-angle, + "hands-helping": $fa-var-hands-helping, + "location-dot": $fa-var-location-dot, + "map-marker-alt": $fa-var-map-marker-alt, + "file": $fa-var-file, + "greater-than": $fa-var-greater-than, + "person-swimming": $fa-var-person-swimming, + "swimmer": $fa-var-swimmer, + "arrow-down": $fa-var-arrow-down, + "droplet": $fa-var-droplet, + "tint": $fa-var-tint, + "eraser": $fa-var-eraser, + "earth-americas": $fa-var-earth-americas, + "earth": $fa-var-earth, + "earth-america": $fa-var-earth-america, + "globe-americas": $fa-var-globe-americas, + "person-burst": $fa-var-person-burst, + "dove": $fa-var-dove, + "battery-empty": $fa-var-battery-empty, + "battery-0": $fa-var-battery-0, + "socks": $fa-var-socks, + "inbox": $fa-var-inbox, + "section": $fa-var-section, + "gauge-high": $fa-var-gauge-high, + "tachometer-alt": $fa-var-tachometer-alt, + "tachometer-alt-fast": $fa-var-tachometer-alt-fast, + "envelope-open-text": $fa-var-envelope-open-text, + "hospital": $fa-var-hospital, + "hospital-alt": $fa-var-hospital-alt, + "hospital-wide": $fa-var-hospital-wide, + "wine-bottle": $fa-var-wine-bottle, + "chess-rook": $fa-var-chess-rook, + "bars-staggered": $fa-var-bars-staggered, + "reorder": $fa-var-reorder, + "stream": $fa-var-stream, + "dharmachakra": $fa-var-dharmachakra, + "hotdog": $fa-var-hotdog, + "person-walking-with-cane": $fa-var-person-walking-with-cane, + "blind": $fa-var-blind, + "drum": $fa-var-drum, + "ice-cream": $fa-var-ice-cream, + "heart-circle-bolt": $fa-var-heart-circle-bolt, + "fax": $fa-var-fax, + "paragraph": $fa-var-paragraph, + "check-to-slot": $fa-var-check-to-slot, + "vote-yea": $fa-var-vote-yea, + "star-half": $fa-var-star-half, + "boxes-stacked": $fa-var-boxes-stacked, + "boxes": $fa-var-boxes, + "boxes-alt": $fa-var-boxes-alt, + "link": $fa-var-link, + "chain": $fa-var-chain, + "ear-listen": $fa-var-ear-listen, + "assistive-listening-systems": $fa-var-assistive-listening-systems, + "tree-city": $fa-var-tree-city, + "play": $fa-var-play, + "font": $fa-var-font, + "rupiah-sign": $fa-var-rupiah-sign, + "magnifying-glass": $fa-var-magnifying-glass, + "search": $fa-var-search, + "table-tennis-paddle-ball": $fa-var-table-tennis-paddle-ball, + "ping-pong-paddle-ball": $fa-var-ping-pong-paddle-ball, + "table-tennis": $fa-var-table-tennis, + "person-dots-from-line": $fa-var-person-dots-from-line, + "diagnoses": $fa-var-diagnoses, + "trash-can-arrow-up": $fa-var-trash-can-arrow-up, + "trash-restore-alt": $fa-var-trash-restore-alt, + "naira-sign": $fa-var-naira-sign, + "cart-arrow-down": $fa-var-cart-arrow-down, + "walkie-talkie": $fa-var-walkie-talkie, + "file-pen": $fa-var-file-pen, + "file-edit": $fa-var-file-edit, + "receipt": $fa-var-receipt, + "square-pen": $fa-var-square-pen, + "pen-square": $fa-var-pen-square, + "pencil-square": $fa-var-pencil-square, + "suitcase-rolling": $fa-var-suitcase-rolling, + "person-circle-exclamation": $fa-var-person-circle-exclamation, + "chevron-down": $fa-var-chevron-down, + "battery-full": $fa-var-battery-full, + "battery": $fa-var-battery, + "battery-5": $fa-var-battery-5, + "skull-crossbones": $fa-var-skull-crossbones, + "code-compare": $fa-var-code-compare, + "list-ul": $fa-var-list-ul, + "list-dots": $fa-var-list-dots, + "school-lock": $fa-var-school-lock, + "tower-cell": $fa-var-tower-cell, + "down-long": $fa-var-down-long, + "long-arrow-alt-down": $fa-var-long-arrow-alt-down, + "ranking-star": $fa-var-ranking-star, + "chess-king": $fa-var-chess-king, + "person-harassing": $fa-var-person-harassing, + "brazilian-real-sign": $fa-var-brazilian-real-sign, + "landmark-dome": $fa-var-landmark-dome, + "landmark-alt": $fa-var-landmark-alt, + "arrow-up": $fa-var-arrow-up, + "tv": $fa-var-tv, + "television": $fa-var-television, + "tv-alt": $fa-var-tv-alt, + "shrimp": $fa-var-shrimp, + "list-check": $fa-var-list-check, + "tasks": $fa-var-tasks, + "jug-detergent": $fa-var-jug-detergent, + "circle-user": $fa-var-circle-user, + "user-circle": $fa-var-user-circle, + "user-shield": $fa-var-user-shield, + "wind": $fa-var-wind, + "car-burst": $fa-var-car-burst, + "car-crash": $fa-var-car-crash, + "y": $fa-var-y, + "person-snowboarding": $fa-var-person-snowboarding, + "snowboarding": $fa-var-snowboarding, + "truck-fast": $fa-var-truck-fast, + "shipping-fast": $fa-var-shipping-fast, + "fish": $fa-var-fish, + "user-graduate": $fa-var-user-graduate, + "circle-half-stroke": $fa-var-circle-half-stroke, + "adjust": $fa-var-adjust, + "clapperboard": $fa-var-clapperboard, + "circle-radiation": $fa-var-circle-radiation, + "radiation-alt": $fa-var-radiation-alt, + "baseball": $fa-var-baseball, + "baseball-ball": $fa-var-baseball-ball, + "jet-fighter-up": $fa-var-jet-fighter-up, + "diagram-project": $fa-var-diagram-project, + "project-diagram": $fa-var-project-diagram, + "copy": $fa-var-copy, + "volume-xmark": $fa-var-volume-xmark, + "volume-mute": $fa-var-volume-mute, + "volume-times": $fa-var-volume-times, + "hand-sparkles": $fa-var-hand-sparkles, + "grip": $fa-var-grip, + "grip-horizontal": $fa-var-grip-horizontal, + "share-from-square": $fa-var-share-from-square, + "share-square": $fa-var-share-square, + "child-combatant": $fa-var-child-combatant, + "child-rifle": $fa-var-child-rifle, + "gun": $fa-var-gun, + "square-phone": $fa-var-square-phone, + "phone-square": $fa-var-phone-square, + "plus": $fa-var-plus, + "add": $fa-var-add, + "expand": $fa-var-expand, + "computer": $fa-var-computer, + "xmark": $fa-var-xmark, + "close": $fa-var-close, + "multiply": $fa-var-multiply, + "remove": $fa-var-remove, + "times": $fa-var-times, + "arrows-up-down-left-right": $fa-var-arrows-up-down-left-right, + "arrows": $fa-var-arrows, + "chalkboard-user": $fa-var-chalkboard-user, + "chalkboard-teacher": $fa-var-chalkboard-teacher, + "peso-sign": $fa-var-peso-sign, + "building-shield": $fa-var-building-shield, + "baby": $fa-var-baby, + "users-line": $fa-var-users-line, + "quote-left": $fa-var-quote-left, + "quote-left-alt": $fa-var-quote-left-alt, + "tractor": $fa-var-tractor, + "trash-arrow-up": $fa-var-trash-arrow-up, + "trash-restore": $fa-var-trash-restore, + "arrow-down-up-lock": $fa-var-arrow-down-up-lock, + "lines-leaning": $fa-var-lines-leaning, + "ruler-combined": $fa-var-ruler-combined, + "copyright": $fa-var-copyright, + "equals": $fa-var-equals, + "blender": $fa-var-blender, + "teeth": $fa-var-teeth, + "shekel-sign": $fa-var-shekel-sign, + "ils": $fa-var-ils, + "shekel": $fa-var-shekel, + "sheqel": $fa-var-sheqel, + "sheqel-sign": $fa-var-sheqel-sign, + "map": $fa-var-map, + "rocket": $fa-var-rocket, + "photo-film": $fa-var-photo-film, + "photo-video": $fa-var-photo-video, + "folder-minus": $fa-var-folder-minus, + "store": $fa-var-store, + "arrow-trend-up": $fa-var-arrow-trend-up, + "plug-circle-minus": $fa-var-plug-circle-minus, + "sign-hanging": $fa-var-sign-hanging, + "sign": $fa-var-sign, + "bezier-curve": $fa-var-bezier-curve, + "bell-slash": $fa-var-bell-slash, + "tablet": $fa-var-tablet, + "tablet-android": $fa-var-tablet-android, + "school-flag": $fa-var-school-flag, + "fill": $fa-var-fill, + "angle-up": $fa-var-angle-up, + "drumstick-bite": $fa-var-drumstick-bite, + "holly-berry": $fa-var-holly-berry, + "chevron-left": $fa-var-chevron-left, + "bacteria": $fa-var-bacteria, + "hand-lizard": $fa-var-hand-lizard, + "notdef": $fa-var-notdef, + "disease": $fa-var-disease, + "briefcase-medical": $fa-var-briefcase-medical, + "genderless": $fa-var-genderless, + "chevron-right": $fa-var-chevron-right, + "retweet": $fa-var-retweet, + "car-rear": $fa-var-car-rear, + "car-alt": $fa-var-car-alt, + "pump-soap": $fa-var-pump-soap, + "video-slash": $fa-var-video-slash, + "battery-quarter": $fa-var-battery-quarter, + "battery-2": $fa-var-battery-2, + "radio": $fa-var-radio, + "baby-carriage": $fa-var-baby-carriage, + "carriage-baby": $fa-var-carriage-baby, + "traffic-light": $fa-var-traffic-light, + "thermometer": $fa-var-thermometer, + "vr-cardboard": $fa-var-vr-cardboard, + "hand-middle-finger": $fa-var-hand-middle-finger, + "percent": $fa-var-percent, + "percentage": $fa-var-percentage, + "truck-moving": $fa-var-truck-moving, + "glass-water-droplet": $fa-var-glass-water-droplet, + "display": $fa-var-display, + "face-smile": $fa-var-face-smile, + "smile": $fa-var-smile, + "thumbtack": $fa-var-thumbtack, + "thumb-tack": $fa-var-thumb-tack, + "trophy": $fa-var-trophy, + "person-praying": $fa-var-person-praying, + "pray": $fa-var-pray, + "hammer": $fa-var-hammer, + "hand-peace": $fa-var-hand-peace, + "rotate": $fa-var-rotate, + "sync-alt": $fa-var-sync-alt, + "spinner": $fa-var-spinner, + "robot": $fa-var-robot, + "peace": $fa-var-peace, + "gears": $fa-var-gears, + "cogs": $fa-var-cogs, + "warehouse": $fa-var-warehouse, + "arrow-up-right-dots": $fa-var-arrow-up-right-dots, + "splotch": $fa-var-splotch, + "face-grin-hearts": $fa-var-face-grin-hearts, + "grin-hearts": $fa-var-grin-hearts, + "dice-four": $fa-var-dice-four, + "sim-card": $fa-var-sim-card, + "transgender": $fa-var-transgender, + "transgender-alt": $fa-var-transgender-alt, + "mercury": $fa-var-mercury, + "arrow-turn-down": $fa-var-arrow-turn-down, + "level-down": $fa-var-level-down, + "person-falling-burst": $fa-var-person-falling-burst, + "award": $fa-var-award, + "ticket-simple": $fa-var-ticket-simple, + "ticket-alt": $fa-var-ticket-alt, + "building": $fa-var-building, + "angles-left": $fa-var-angles-left, + "angle-double-left": $fa-var-angle-double-left, + "qrcode": $fa-var-qrcode, + "clock-rotate-left": $fa-var-clock-rotate-left, + "history": $fa-var-history, + "face-grin-beam-sweat": $fa-var-face-grin-beam-sweat, + "grin-beam-sweat": $fa-var-grin-beam-sweat, + "file-export": $fa-var-file-export, + "arrow-right-from-file": $fa-var-arrow-right-from-file, + "shield": $fa-var-shield, + "shield-blank": $fa-var-shield-blank, + "arrow-up-short-wide": $fa-var-arrow-up-short-wide, + "sort-amount-up-alt": $fa-var-sort-amount-up-alt, + "house-medical": $fa-var-house-medical, + "golf-ball-tee": $fa-var-golf-ball-tee, + "golf-ball": $fa-var-golf-ball, + "circle-chevron-left": $fa-var-circle-chevron-left, + "chevron-circle-left": $fa-var-chevron-circle-left, + "house-chimney-window": $fa-var-house-chimney-window, + "pen-nib": $fa-var-pen-nib, + "tent-arrow-turn-left": $fa-var-tent-arrow-turn-left, + "tents": $fa-var-tents, + "wand-magic": $fa-var-wand-magic, + "magic": $fa-var-magic, + "dog": $fa-var-dog, + "carrot": $fa-var-carrot, + "moon": $fa-var-moon, + "wine-glass-empty": $fa-var-wine-glass-empty, + "wine-glass-alt": $fa-var-wine-glass-alt, + "cheese": $fa-var-cheese, + "yin-yang": $fa-var-yin-yang, + "music": $fa-var-music, + "code-commit": $fa-var-code-commit, + "temperature-low": $fa-var-temperature-low, + "person-biking": $fa-var-person-biking, + "biking": $fa-var-biking, + "broom": $fa-var-broom, + "shield-heart": $fa-var-shield-heart, + "gopuram": $fa-var-gopuram, + "earth-oceania": $fa-var-earth-oceania, + "globe-oceania": $fa-var-globe-oceania, + "square-xmark": $fa-var-square-xmark, + "times-square": $fa-var-times-square, + "xmark-square": $fa-var-xmark-square, + "hashtag": $fa-var-hashtag, + "up-right-and-down-left-from-center": $fa-var-up-right-and-down-left-from-center, + "expand-alt": $fa-var-expand-alt, + "oil-can": $fa-var-oil-can, + "t": $fa-var-t, + "hippo": $fa-var-hippo, + "chart-column": $fa-var-chart-column, + "infinity": $fa-var-infinity, + "vial-circle-check": $fa-var-vial-circle-check, + "person-arrow-down-to-line": $fa-var-person-arrow-down-to-line, + "voicemail": $fa-var-voicemail, + "fan": $fa-var-fan, + "person-walking-luggage": $fa-var-person-walking-luggage, + "up-down": $fa-var-up-down, + "arrows-alt-v": $fa-var-arrows-alt-v, + "cloud-moon-rain": $fa-var-cloud-moon-rain, + "calendar": $fa-var-calendar, + "trailer": $fa-var-trailer, + "bahai": $fa-var-bahai, + "haykal": $fa-var-haykal, + "sd-card": $fa-var-sd-card, + "dragon": $fa-var-dragon, + "shoe-prints": $fa-var-shoe-prints, + "circle-plus": $fa-var-circle-plus, + "plus-circle": $fa-var-plus-circle, + "face-grin-tongue-wink": $fa-var-face-grin-tongue-wink, + "grin-tongue-wink": $fa-var-grin-tongue-wink, + "hand-holding": $fa-var-hand-holding, + "plug-circle-exclamation": $fa-var-plug-circle-exclamation, + "link-slash": $fa-var-link-slash, + "chain-broken": $fa-var-chain-broken, + "chain-slash": $fa-var-chain-slash, + "unlink": $fa-var-unlink, + "clone": $fa-var-clone, + "person-walking-arrow-loop-left": $fa-var-person-walking-arrow-loop-left, + "arrow-up-z-a": $fa-var-arrow-up-z-a, + "sort-alpha-up-alt": $fa-var-sort-alpha-up-alt, + "fire-flame-curved": $fa-var-fire-flame-curved, + "fire-alt": $fa-var-fire-alt, + "tornado": $fa-var-tornado, + "file-circle-plus": $fa-var-file-circle-plus, + "book-quran": $fa-var-book-quran, + "quran": $fa-var-quran, + "anchor": $fa-var-anchor, + "border-all": $fa-var-border-all, + "face-angry": $fa-var-face-angry, + "angry": $fa-var-angry, + "cookie-bite": $fa-var-cookie-bite, + "arrow-trend-down": $fa-var-arrow-trend-down, + "rss": $fa-var-rss, + "feed": $fa-var-feed, + "draw-polygon": $fa-var-draw-polygon, + "scale-balanced": $fa-var-scale-balanced, + "balance-scale": $fa-var-balance-scale, + "gauge-simple-high": $fa-var-gauge-simple-high, + "tachometer": $fa-var-tachometer, + "tachometer-fast": $fa-var-tachometer-fast, + "shower": $fa-var-shower, + "desktop": $fa-var-desktop, + "desktop-alt": $fa-var-desktop-alt, + "m": $fa-var-m, + "table-list": $fa-var-table-list, + "th-list": $fa-var-th-list, + "comment-sms": $fa-var-comment-sms, + "sms": $fa-var-sms, + "book": $fa-var-book, + "user-plus": $fa-var-user-plus, + "check": $fa-var-check, + "battery-three-quarters": $fa-var-battery-three-quarters, + "battery-4": $fa-var-battery-4, + "house-circle-check": $fa-var-house-circle-check, + "angle-left": $fa-var-angle-left, + "diagram-successor": $fa-var-diagram-successor, + "truck-arrow-right": $fa-var-truck-arrow-right, + "arrows-split-up-and-left": $fa-var-arrows-split-up-and-left, + "hand-fist": $fa-var-hand-fist, + "fist-raised": $fa-var-fist-raised, + "cloud-moon": $fa-var-cloud-moon, + "briefcase": $fa-var-briefcase, + "person-falling": $fa-var-person-falling, + "image-portrait": $fa-var-image-portrait, + "portrait": $fa-var-portrait, + "user-tag": $fa-var-user-tag, + "rug": $fa-var-rug, + "earth-europe": $fa-var-earth-europe, + "globe-europe": $fa-var-globe-europe, + "cart-flatbed-suitcase": $fa-var-cart-flatbed-suitcase, + "luggage-cart": $fa-var-luggage-cart, + "rectangle-xmark": $fa-var-rectangle-xmark, + "rectangle-times": $fa-var-rectangle-times, + "times-rectangle": $fa-var-times-rectangle, + "window-close": $fa-var-window-close, + "baht-sign": $fa-var-baht-sign, + "book-open": $fa-var-book-open, + "book-journal-whills": $fa-var-book-journal-whills, + "journal-whills": $fa-var-journal-whills, + "handcuffs": $fa-var-handcuffs, + "triangle-exclamation": $fa-var-triangle-exclamation, + "exclamation-triangle": $fa-var-exclamation-triangle, + "warning": $fa-var-warning, + "database": $fa-var-database, + "share": $fa-var-share, + "arrow-turn-right": $fa-var-arrow-turn-right, + "mail-forward": $fa-var-mail-forward, + "bottle-droplet": $fa-var-bottle-droplet, + "mask-face": $fa-var-mask-face, + "hill-rockslide": $fa-var-hill-rockslide, + "right-left": $fa-var-right-left, + "exchange-alt": $fa-var-exchange-alt, + "paper-plane": $fa-var-paper-plane, + "road-circle-exclamation": $fa-var-road-circle-exclamation, + "dungeon": $fa-var-dungeon, + "align-right": $fa-var-align-right, + "money-bill-1-wave": $fa-var-money-bill-1-wave, + "money-bill-wave-alt": $fa-var-money-bill-wave-alt, + "life-ring": $fa-var-life-ring, + "hands": $fa-var-hands, + "sign-language": $fa-var-sign-language, + "signing": $fa-var-signing, + "calendar-day": $fa-var-calendar-day, + "water-ladder": $fa-var-water-ladder, + "ladder-water": $fa-var-ladder-water, + "swimming-pool": $fa-var-swimming-pool, + "arrows-up-down": $fa-var-arrows-up-down, + "arrows-v": $fa-var-arrows-v, + "face-grimace": $fa-var-face-grimace, + "grimace": $fa-var-grimace, + "wheelchair-move": $fa-var-wheelchair-move, + "wheelchair-alt": $fa-var-wheelchair-alt, + "turn-down": $fa-var-turn-down, + "level-down-alt": $fa-var-level-down-alt, + "person-walking-arrow-right": $fa-var-person-walking-arrow-right, + "square-envelope": $fa-var-square-envelope, + "envelope-square": $fa-var-envelope-square, + "dice": $fa-var-dice, + "bowling-ball": $fa-var-bowling-ball, + "brain": $fa-var-brain, + "bandage": $fa-var-bandage, + "band-aid": $fa-var-band-aid, + "calendar-minus": $fa-var-calendar-minus, + "circle-xmark": $fa-var-circle-xmark, + "times-circle": $fa-var-times-circle, + "xmark-circle": $fa-var-xmark-circle, + "gifts": $fa-var-gifts, + "hotel": $fa-var-hotel, + "earth-asia": $fa-var-earth-asia, + "globe-asia": $fa-var-globe-asia, + "id-card-clip": $fa-var-id-card-clip, + "id-card-alt": $fa-var-id-card-alt, + "magnifying-glass-plus": $fa-var-magnifying-glass-plus, + "search-plus": $fa-var-search-plus, + "thumbs-up": $fa-var-thumbs-up, + "user-clock": $fa-var-user-clock, + "hand-dots": $fa-var-hand-dots, + "allergies": $fa-var-allergies, + "file-invoice": $fa-var-file-invoice, + "window-minimize": $fa-var-window-minimize, + "mug-saucer": $fa-var-mug-saucer, + "coffee": $fa-var-coffee, + "brush": $fa-var-brush, + "mask": $fa-var-mask, + "magnifying-glass-minus": $fa-var-magnifying-glass-minus, + "search-minus": $fa-var-search-minus, + "ruler-vertical": $fa-var-ruler-vertical, + "user-large": $fa-var-user-large, + "user-alt": $fa-var-user-alt, + "train-tram": $fa-var-train-tram, + "user-nurse": $fa-var-user-nurse, + "syringe": $fa-var-syringe, + "cloud-sun": $fa-var-cloud-sun, + "stopwatch-20": $fa-var-stopwatch-20, + "square-full": $fa-var-square-full, + "magnet": $fa-var-magnet, + "jar": $fa-var-jar, + "note-sticky": $fa-var-note-sticky, + "sticky-note": $fa-var-sticky-note, + "bug-slash": $fa-var-bug-slash, + "arrow-up-from-water-pump": $fa-var-arrow-up-from-water-pump, + "bone": $fa-var-bone, + "user-injured": $fa-var-user-injured, + "face-sad-tear": $fa-var-face-sad-tear, + "sad-tear": $fa-var-sad-tear, + "plane": $fa-var-plane, + "tent-arrows-down": $fa-var-tent-arrows-down, + "exclamation": $fa-var-exclamation, + "arrows-spin": $fa-var-arrows-spin, + "print": $fa-var-print, + "turkish-lira-sign": $fa-var-turkish-lira-sign, + "try": $fa-var-try, + "turkish-lira": $fa-var-turkish-lira, + "dollar-sign": $fa-var-dollar-sign, + "dollar": $fa-var-dollar, + "usd": $fa-var-usd, + "x": $fa-var-x, + "magnifying-glass-dollar": $fa-var-magnifying-glass-dollar, + "search-dollar": $fa-var-search-dollar, + "users-gear": $fa-var-users-gear, + "users-cog": $fa-var-users-cog, + "person-military-pointing": $fa-var-person-military-pointing, + "building-columns": $fa-var-building-columns, + "bank": $fa-var-bank, + "institution": $fa-var-institution, + "museum": $fa-var-museum, + "university": $fa-var-university, + "umbrella": $fa-var-umbrella, + "trowel": $fa-var-trowel, + "d": $fa-var-d, + "stapler": $fa-var-stapler, + "masks-theater": $fa-var-masks-theater, + "theater-masks": $fa-var-theater-masks, + "kip-sign": $fa-var-kip-sign, + "hand-point-left": $fa-var-hand-point-left, + "handshake-simple": $fa-var-handshake-simple, + "handshake-alt": $fa-var-handshake-alt, + "jet-fighter": $fa-var-jet-fighter, + "fighter-jet": $fa-var-fighter-jet, + "square-share-nodes": $fa-var-square-share-nodes, + "share-alt-square": $fa-var-share-alt-square, + "barcode": $fa-var-barcode, + "plus-minus": $fa-var-plus-minus, + "video": $fa-var-video, + "video-camera": $fa-var-video-camera, + "graduation-cap": $fa-var-graduation-cap, + "mortar-board": $fa-var-mortar-board, + "hand-holding-medical": $fa-var-hand-holding-medical, + "person-circle-check": $fa-var-person-circle-check, + "turn-up": $fa-var-turn-up, + "level-up-alt": $fa-var-level-up-alt, +); + +$fa-brand-icons: ( + "monero": $fa-var-monero, + "hooli": $fa-var-hooli, + "yelp": $fa-var-yelp, + "cc-visa": $fa-var-cc-visa, + "lastfm": $fa-var-lastfm, + "shopware": $fa-var-shopware, + "creative-commons-nc": $fa-var-creative-commons-nc, + "aws": $fa-var-aws, + "redhat": $fa-var-redhat, + "yoast": $fa-var-yoast, + "cloudflare": $fa-var-cloudflare, + "ups": $fa-var-ups, + "wpexplorer": $fa-var-wpexplorer, + "dyalog": $fa-var-dyalog, + "bity": $fa-var-bity, + "stackpath": $fa-var-stackpath, + "buysellads": $fa-var-buysellads, + "first-order": $fa-var-first-order, + "modx": $fa-var-modx, + "guilded": $fa-var-guilded, + "vnv": $fa-var-vnv, + "square-js": $fa-var-square-js, + "js-square": $fa-var-js-square, + "microsoft": $fa-var-microsoft, + "qq": $fa-var-qq, + "orcid": $fa-var-orcid, + "java": $fa-var-java, + "invision": $fa-var-invision, + "creative-commons-pd-alt": $fa-var-creative-commons-pd-alt, + "centercode": $fa-var-centercode, + "glide-g": $fa-var-glide-g, + "drupal": $fa-var-drupal, + "hire-a-helper": $fa-var-hire-a-helper, + "creative-commons-by": $fa-var-creative-commons-by, + "unity": $fa-var-unity, + "whmcs": $fa-var-whmcs, + "rocketchat": $fa-var-rocketchat, + "vk": $fa-var-vk, + "untappd": $fa-var-untappd, + "mailchimp": $fa-var-mailchimp, + "css3-alt": $fa-var-css3-alt, + "square-reddit": $fa-var-square-reddit, + "reddit-square": $fa-var-reddit-square, + "vimeo-v": $fa-var-vimeo-v, + "contao": $fa-var-contao, + "square-font-awesome": $fa-var-square-font-awesome, + "deskpro": $fa-var-deskpro, + "sistrix": $fa-var-sistrix, + "square-instagram": $fa-var-square-instagram, + "instagram-square": $fa-var-instagram-square, + "battle-net": $fa-var-battle-net, + "the-red-yeti": $fa-var-the-red-yeti, + "square-hacker-news": $fa-var-square-hacker-news, + "hacker-news-square": $fa-var-hacker-news-square, + "edge": $fa-var-edge, + "napster": $fa-var-napster, + "square-snapchat": $fa-var-square-snapchat, + "snapchat-square": $fa-var-snapchat-square, + "google-plus-g": $fa-var-google-plus-g, + "artstation": $fa-var-artstation, + "markdown": $fa-var-markdown, + "sourcetree": $fa-var-sourcetree, + "google-plus": $fa-var-google-plus, + "diaspora": $fa-var-diaspora, + "foursquare": $fa-var-foursquare, + "stack-overflow": $fa-var-stack-overflow, + "github-alt": $fa-var-github-alt, + "phoenix-squadron": $fa-var-phoenix-squadron, + "pagelines": $fa-var-pagelines, + "algolia": $fa-var-algolia, + "red-river": $fa-var-red-river, + "creative-commons-sa": $fa-var-creative-commons-sa, + "safari": $fa-var-safari, + "google": $fa-var-google, + "square-font-awesome-stroke": $fa-var-square-font-awesome-stroke, + "font-awesome-alt": $fa-var-font-awesome-alt, + "atlassian": $fa-var-atlassian, + "linkedin-in": $fa-var-linkedin-in, + "digital-ocean": $fa-var-digital-ocean, + "nimblr": $fa-var-nimblr, + "chromecast": $fa-var-chromecast, + "evernote": $fa-var-evernote, + "hacker-news": $fa-var-hacker-news, + "creative-commons-sampling": $fa-var-creative-commons-sampling, + "adversal": $fa-var-adversal, + "creative-commons": $fa-var-creative-commons, + "watchman-monitoring": $fa-var-watchman-monitoring, + "fonticons": $fa-var-fonticons, + "weixin": $fa-var-weixin, + "shirtsinbulk": $fa-var-shirtsinbulk, + "codepen": $fa-var-codepen, + "git-alt": $fa-var-git-alt, + "lyft": $fa-var-lyft, + "rev": $fa-var-rev, + "windows": $fa-var-windows, + "wizards-of-the-coast": $fa-var-wizards-of-the-coast, + "square-viadeo": $fa-var-square-viadeo, + "viadeo-square": $fa-var-viadeo-square, + "meetup": $fa-var-meetup, + "centos": $fa-var-centos, + "adn": $fa-var-adn, + "cloudsmith": $fa-var-cloudsmith, + "pied-piper-alt": $fa-var-pied-piper-alt, + "square-dribbble": $fa-var-square-dribbble, + "dribbble-square": $fa-var-dribbble-square, + "codiepie": $fa-var-codiepie, + "node": $fa-var-node, + "mix": $fa-var-mix, + "steam": $fa-var-steam, + "cc-apple-pay": $fa-var-cc-apple-pay, + "scribd": $fa-var-scribd, + "openid": $fa-var-openid, + "instalod": $fa-var-instalod, + "expeditedssl": $fa-var-expeditedssl, + "sellcast": $fa-var-sellcast, + "square-twitter": $fa-var-square-twitter, + "twitter-square": $fa-var-twitter-square, + "r-project": $fa-var-r-project, + "delicious": $fa-var-delicious, + "freebsd": $fa-var-freebsd, + "vuejs": $fa-var-vuejs, + "accusoft": $fa-var-accusoft, + "ioxhost": $fa-var-ioxhost, + "fonticons-fi": $fa-var-fonticons-fi, + "app-store": $fa-var-app-store, + "cc-mastercard": $fa-var-cc-mastercard, + "itunes-note": $fa-var-itunes-note, + "golang": $fa-var-golang, + "kickstarter": $fa-var-kickstarter, + "grav": $fa-var-grav, + "weibo": $fa-var-weibo, + "uncharted": $fa-var-uncharted, + "firstdraft": $fa-var-firstdraft, + "square-youtube": $fa-var-square-youtube, + "youtube-square": $fa-var-youtube-square, + "wikipedia-w": $fa-var-wikipedia-w, + "wpressr": $fa-var-wpressr, + "rendact": $fa-var-rendact, + "angellist": $fa-var-angellist, + "galactic-republic": $fa-var-galactic-republic, + "nfc-directional": $fa-var-nfc-directional, + "skype": $fa-var-skype, + "joget": $fa-var-joget, + "fedora": $fa-var-fedora, + "stripe-s": $fa-var-stripe-s, + "meta": $fa-var-meta, + "laravel": $fa-var-laravel, + "hotjar": $fa-var-hotjar, + "bluetooth-b": $fa-var-bluetooth-b, + "sticker-mule": $fa-var-sticker-mule, + "creative-commons-zero": $fa-var-creative-commons-zero, + "hips": $fa-var-hips, + "behance": $fa-var-behance, + "reddit": $fa-var-reddit, + "discord": $fa-var-discord, + "chrome": $fa-var-chrome, + "app-store-ios": $fa-var-app-store-ios, + "cc-discover": $fa-var-cc-discover, + "wpbeginner": $fa-var-wpbeginner, + "confluence": $fa-var-confluence, + "mdb": $fa-var-mdb, + "dochub": $fa-var-dochub, + "accessible-icon": $fa-var-accessible-icon, + "ebay": $fa-var-ebay, + "amazon": $fa-var-amazon, + "unsplash": $fa-var-unsplash, + "yarn": $fa-var-yarn, + "square-steam": $fa-var-square-steam, + "steam-square": $fa-var-steam-square, + "500px": $fa-var-500px, + "square-vimeo": $fa-var-square-vimeo, + "vimeo-square": $fa-var-vimeo-square, + "asymmetrik": $fa-var-asymmetrik, + "font-awesome": $fa-var-font-awesome, + "font-awesome-flag": $fa-var-font-awesome-flag, + "font-awesome-logo-full": $fa-var-font-awesome-logo-full, + "gratipay": $fa-var-gratipay, + "apple": $fa-var-apple, + "hive": $fa-var-hive, + "gitkraken": $fa-var-gitkraken, + "keybase": $fa-var-keybase, + "apple-pay": $fa-var-apple-pay, + "padlet": $fa-var-padlet, + "amazon-pay": $fa-var-amazon-pay, + "square-github": $fa-var-square-github, + "github-square": $fa-var-github-square, + "stumbleupon": $fa-var-stumbleupon, + "fedex": $fa-var-fedex, + "phoenix-framework": $fa-var-phoenix-framework, + "shopify": $fa-var-shopify, + "neos": $fa-var-neos, + "hackerrank": $fa-var-hackerrank, + "researchgate": $fa-var-researchgate, + "swift": $fa-var-swift, + "angular": $fa-var-angular, + "speakap": $fa-var-speakap, + "angrycreative": $fa-var-angrycreative, + "y-combinator": $fa-var-y-combinator, + "empire": $fa-var-empire, + "envira": $fa-var-envira, + "square-gitlab": $fa-var-square-gitlab, + "gitlab-square": $fa-var-gitlab-square, + "studiovinari": $fa-var-studiovinari, + "pied-piper": $fa-var-pied-piper, + "wordpress": $fa-var-wordpress, + "product-hunt": $fa-var-product-hunt, + "firefox": $fa-var-firefox, + "linode": $fa-var-linode, + "goodreads": $fa-var-goodreads, + "square-odnoklassniki": $fa-var-square-odnoklassniki, + "odnoklassniki-square": $fa-var-odnoklassniki-square, + "jsfiddle": $fa-var-jsfiddle, + "sith": $fa-var-sith, + "themeisle": $fa-var-themeisle, + "page4": $fa-var-page4, + "hashnode": $fa-var-hashnode, + "react": $fa-var-react, + "cc-paypal": $fa-var-cc-paypal, + "squarespace": $fa-var-squarespace, + "cc-stripe": $fa-var-cc-stripe, + "creative-commons-share": $fa-var-creative-commons-share, + "bitcoin": $fa-var-bitcoin, + "keycdn": $fa-var-keycdn, + "opera": $fa-var-opera, + "itch-io": $fa-var-itch-io, + "umbraco": $fa-var-umbraco, + "galactic-senate": $fa-var-galactic-senate, + "ubuntu": $fa-var-ubuntu, + "draft2digital": $fa-var-draft2digital, + "stripe": $fa-var-stripe, + "houzz": $fa-var-houzz, + "gg": $fa-var-gg, + "dhl": $fa-var-dhl, + "square-pinterest": $fa-var-square-pinterest, + "pinterest-square": $fa-var-pinterest-square, + "xing": $fa-var-xing, + "blackberry": $fa-var-blackberry, + "creative-commons-pd": $fa-var-creative-commons-pd, + "playstation": $fa-var-playstation, + "quinscape": $fa-var-quinscape, + "less": $fa-var-less, + "blogger-b": $fa-var-blogger-b, + "opencart": $fa-var-opencart, + "vine": $fa-var-vine, + "paypal": $fa-var-paypal, + "gitlab": $fa-var-gitlab, + "typo3": $fa-var-typo3, + "reddit-alien": $fa-var-reddit-alien, + "yahoo": $fa-var-yahoo, + "dailymotion": $fa-var-dailymotion, + "affiliatetheme": $fa-var-affiliatetheme, + "pied-piper-pp": $fa-var-pied-piper-pp, + "bootstrap": $fa-var-bootstrap, + "odnoklassniki": $fa-var-odnoklassniki, + "nfc-symbol": $fa-var-nfc-symbol, + "ethereum": $fa-var-ethereum, + "speaker-deck": $fa-var-speaker-deck, + "creative-commons-nc-eu": $fa-var-creative-commons-nc-eu, + "patreon": $fa-var-patreon, + "avianex": $fa-var-avianex, + "ello": $fa-var-ello, + "gofore": $fa-var-gofore, + "bimobject": $fa-var-bimobject, + "facebook-f": $fa-var-facebook-f, + "square-google-plus": $fa-var-square-google-plus, + "google-plus-square": $fa-var-google-plus-square, + "mandalorian": $fa-var-mandalorian, + "first-order-alt": $fa-var-first-order-alt, + "osi": $fa-var-osi, + "google-wallet": $fa-var-google-wallet, + "d-and-d-beyond": $fa-var-d-and-d-beyond, + "periscope": $fa-var-periscope, + "fulcrum": $fa-var-fulcrum, + "cloudscale": $fa-var-cloudscale, + "forumbee": $fa-var-forumbee, + "mizuni": $fa-var-mizuni, + "schlix": $fa-var-schlix, + "square-xing": $fa-var-square-xing, + "xing-square": $fa-var-xing-square, + "bandcamp": $fa-var-bandcamp, + "wpforms": $fa-var-wpforms, + "cloudversify": $fa-var-cloudversify, + "usps": $fa-var-usps, + "megaport": $fa-var-megaport, + "magento": $fa-var-magento, + "spotify": $fa-var-spotify, + "optin-monster": $fa-var-optin-monster, + "fly": $fa-var-fly, + "aviato": $fa-var-aviato, + "itunes": $fa-var-itunes, + "cuttlefish": $fa-var-cuttlefish, + "blogger": $fa-var-blogger, + "flickr": $fa-var-flickr, + "viber": $fa-var-viber, + "soundcloud": $fa-var-soundcloud, + "digg": $fa-var-digg, + "tencent-weibo": $fa-var-tencent-weibo, + "symfony": $fa-var-symfony, + "maxcdn": $fa-var-maxcdn, + "etsy": $fa-var-etsy, + "facebook-messenger": $fa-var-facebook-messenger, + "audible": $fa-var-audible, + "think-peaks": $fa-var-think-peaks, + "bilibili": $fa-var-bilibili, + "erlang": $fa-var-erlang, + "cotton-bureau": $fa-var-cotton-bureau, + "dashcube": $fa-var-dashcube, + "42-group": $fa-var-42-group, + "innosoft": $fa-var-innosoft, + "stack-exchange": $fa-var-stack-exchange, + "elementor": $fa-var-elementor, + "square-pied-piper": $fa-var-square-pied-piper, + "pied-piper-square": $fa-var-pied-piper-square, + "creative-commons-nd": $fa-var-creative-commons-nd, + "palfed": $fa-var-palfed, + "superpowers": $fa-var-superpowers, + "resolving": $fa-var-resolving, + "xbox": $fa-var-xbox, + "searchengin": $fa-var-searchengin, + "tiktok": $fa-var-tiktok, + "square-facebook": $fa-var-square-facebook, + "facebook-square": $fa-var-facebook-square, + "renren": $fa-var-renren, + "linux": $fa-var-linux, + "glide": $fa-var-glide, + "linkedin": $fa-var-linkedin, + "hubspot": $fa-var-hubspot, + "deploydog": $fa-var-deploydog, + "twitch": $fa-var-twitch, + "ravelry": $fa-var-ravelry, + "mixer": $fa-var-mixer, + "square-lastfm": $fa-var-square-lastfm, + "lastfm-square": $fa-var-lastfm-square, + "vimeo": $fa-var-vimeo, + "mendeley": $fa-var-mendeley, + "uniregistry": $fa-var-uniregistry, + "figma": $fa-var-figma, + "creative-commons-remix": $fa-var-creative-commons-remix, + "cc-amazon-pay": $fa-var-cc-amazon-pay, + "dropbox": $fa-var-dropbox, + "instagram": $fa-var-instagram, + "cmplid": $fa-var-cmplid, + "facebook": $fa-var-facebook, + "gripfire": $fa-var-gripfire, + "jedi-order": $fa-var-jedi-order, + "uikit": $fa-var-uikit, + "fort-awesome-alt": $fa-var-fort-awesome-alt, + "phabricator": $fa-var-phabricator, + "ussunnah": $fa-var-ussunnah, + "earlybirds": $fa-var-earlybirds, + "trade-federation": $fa-var-trade-federation, + "autoprefixer": $fa-var-autoprefixer, + "whatsapp": $fa-var-whatsapp, + "slideshare": $fa-var-slideshare, + "google-play": $fa-var-google-play, + "viadeo": $fa-var-viadeo, + "line": $fa-var-line, + "google-drive": $fa-var-google-drive, + "servicestack": $fa-var-servicestack, + "simplybuilt": $fa-var-simplybuilt, + "bitbucket": $fa-var-bitbucket, + "imdb": $fa-var-imdb, + "deezer": $fa-var-deezer, + "raspberry-pi": $fa-var-raspberry-pi, + "jira": $fa-var-jira, + "docker": $fa-var-docker, + "screenpal": $fa-var-screenpal, + "bluetooth": $fa-var-bluetooth, + "gitter": $fa-var-gitter, + "d-and-d": $fa-var-d-and-d, + "microblog": $fa-var-microblog, + "cc-diners-club": $fa-var-cc-diners-club, + "gg-circle": $fa-var-gg-circle, + "pied-piper-hat": $fa-var-pied-piper-hat, + "kickstarter-k": $fa-var-kickstarter-k, + "yandex": $fa-var-yandex, + "readme": $fa-var-readme, + "html5": $fa-var-html5, + "sellsy": $fa-var-sellsy, + "sass": $fa-var-sass, + "wirsindhandwerk": $fa-var-wirsindhandwerk, + "wsh": $fa-var-wsh, + "buromobelexperte": $fa-var-buromobelexperte, + "salesforce": $fa-var-salesforce, + "octopus-deploy": $fa-var-octopus-deploy, + "medapps": $fa-var-medapps, + "ns8": $fa-var-ns8, + "pinterest-p": $fa-var-pinterest-p, + "apper": $fa-var-apper, + "fort-awesome": $fa-var-fort-awesome, + "waze": $fa-var-waze, + "cc-jcb": $fa-var-cc-jcb, + "snapchat": $fa-var-snapchat, + "snapchat-ghost": $fa-var-snapchat-ghost, + "fantasy-flight-games": $fa-var-fantasy-flight-games, + "rust": $fa-var-rust, + "wix": $fa-var-wix, + "square-behance": $fa-var-square-behance, + "behance-square": $fa-var-behance-square, + "supple": $fa-var-supple, + "rebel": $fa-var-rebel, + "css3": $fa-var-css3, + "staylinked": $fa-var-staylinked, + "kaggle": $fa-var-kaggle, + "space-awesome": $fa-var-space-awesome, + "deviantart": $fa-var-deviantart, + "cpanel": $fa-var-cpanel, + "goodreads-g": $fa-var-goodreads-g, + "square-git": $fa-var-square-git, + "git-square": $fa-var-git-square, + "square-tumblr": $fa-var-square-tumblr, + "tumblr-square": $fa-var-tumblr-square, + "trello": $fa-var-trello, + "creative-commons-nc-jp": $fa-var-creative-commons-nc-jp, + "get-pocket": $fa-var-get-pocket, + "perbyte": $fa-var-perbyte, + "grunt": $fa-var-grunt, + "weebly": $fa-var-weebly, + "connectdevelop": $fa-var-connectdevelop, + "leanpub": $fa-var-leanpub, + "black-tie": $fa-var-black-tie, + "themeco": $fa-var-themeco, + "python": $fa-var-python, + "android": $fa-var-android, + "bots": $fa-var-bots, + "free-code-camp": $fa-var-free-code-camp, + "hornbill": $fa-var-hornbill, + "js": $fa-var-js, + "ideal": $fa-var-ideal, + "git": $fa-var-git, + "dev": $fa-var-dev, + "sketch": $fa-var-sketch, + "yandex-international": $fa-var-yandex-international, + "cc-amex": $fa-var-cc-amex, + "uber": $fa-var-uber, + "github": $fa-var-github, + "php": $fa-var-php, + "alipay": $fa-var-alipay, + "youtube": $fa-var-youtube, + "skyatlas": $fa-var-skyatlas, + "firefox-browser": $fa-var-firefox-browser, + "replyd": $fa-var-replyd, + "suse": $fa-var-suse, + "jenkins": $fa-var-jenkins, + "twitter": $fa-var-twitter, + "rockrms": $fa-var-rockrms, + "pinterest": $fa-var-pinterest, + "buffer": $fa-var-buffer, + "npm": $fa-var-npm, + "yammer": $fa-var-yammer, + "btc": $fa-var-btc, + "dribbble": $fa-var-dribbble, + "stumbleupon-circle": $fa-var-stumbleupon-circle, + "internet-explorer": $fa-var-internet-explorer, + "stubber": $fa-var-stubber, + "telegram": $fa-var-telegram, + "telegram-plane": $fa-var-telegram-plane, + "old-republic": $fa-var-old-republic, + "odysee": $fa-var-odysee, + "square-whatsapp": $fa-var-square-whatsapp, + "whatsapp-square": $fa-var-whatsapp-square, + "node-js": $fa-var-node-js, + "edge-legacy": $fa-var-edge-legacy, + "slack": $fa-var-slack, + "slack-hash": $fa-var-slack-hash, + "medrt": $fa-var-medrt, + "usb": $fa-var-usb, + "tumblr": $fa-var-tumblr, + "vaadin": $fa-var-vaadin, + "quora": $fa-var-quora, + "reacteurope": $fa-var-reacteurope, + "medium": $fa-var-medium, + "medium-m": $fa-var-medium-m, + "amilia": $fa-var-amilia, + "mixcloud": $fa-var-mixcloud, + "flipboard": $fa-var-flipboard, + "viacoin": $fa-var-viacoin, + "critical-role": $fa-var-critical-role, + "sitrox": $fa-var-sitrox, + "discourse": $fa-var-discourse, + "joomla": $fa-var-joomla, + "mastodon": $fa-var-mastodon, + "airbnb": $fa-var-airbnb, + "wolf-pack-battalion": $fa-var-wolf-pack-battalion, + "buy-n-large": $fa-var-buy-n-large, + "gulp": $fa-var-gulp, + "creative-commons-sampling-plus": $fa-var-creative-commons-sampling-plus, + "strava": $fa-var-strava, + "ember": $fa-var-ember, + "canadian-maple-leaf": $fa-var-canadian-maple-leaf, + "teamspeak": $fa-var-teamspeak, + "pushed": $fa-var-pushed, + "wordpress-simple": $fa-var-wordpress-simple, + "nutritionix": $fa-var-nutritionix, + "wodu": $fa-var-wodu, + "google-pay": $fa-var-google-pay, + "intercom": $fa-var-intercom, + "zhihu": $fa-var-zhihu, + "korvue": $fa-var-korvue, + "pix": $fa-var-pix, + "steam-symbol": $fa-var-steam-symbol, +); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/brands.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/brands.scss new file mode 100644 index 0000000..fcea462 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/brands.scss @@ -0,0 +1,30 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +@import 'functions'; +@import 'variables'; + +:root, :host { + --#{$fa-css-prefix}-style-family-brands: 'Font Awesome 6 Brands'; + --#{$fa-css-prefix}-font-brands: normal 400 1em/1 'Font Awesome 6 Brands'; +} + +@font-face { + font-family: 'Font Awesome 6 Brands'; + font-style: normal; + font-weight: 400; + font-display: $fa-font-display; + src: url('#{$fa-font-path}/fa-brands-400.woff2') format('woff2'), + url('#{$fa-font-path}/fa-brands-400.ttf') format('truetype'); +} + +.fab, +.#{$fa-css-prefix}-brands { + font-weight: 400; +} + +@each $name, $icon in $fa-brand-icons { + .#{$fa-css-prefix}-#{$name}:before { content: unquote("\"#{ $icon }\""); } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/fontawesome.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/fontawesome.scss new file mode 100644 index 0000000..ab4f133 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/fontawesome.scss @@ -0,0 +1,21 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +// Font Awesome core compile (Web Fonts-based) +// ------------------------- + +@import 'functions'; +@import 'variables'; +@import 'mixins'; +@import 'core'; +@import 'sizing'; +@import 'fixed-width'; +@import 'list'; +@import 'bordered-pulled'; +@import 'animated'; +@import 'rotated-flipped'; +@import 'stacked'; +@import 'icons'; +@import 'screen-reader'; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/regular.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/regular.scss new file mode 100644 index 0000000..40d1753 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/regular.scss @@ -0,0 +1,26 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +@import 'functions'; +@import 'variables'; + +:root, :host { + --#{$fa-css-prefix}-style-family-classic: '#{ $fa-style-family }'; + --#{$fa-css-prefix}-font-regular: normal 400 1em/1 '#{ $fa-style-family }'; +} + +@font-face { + font-family: 'Font Awesome 6 Free'; + font-style: normal; + font-weight: 400; + font-display: $fa-font-display; + src: url('#{$fa-font-path}/fa-regular-400.woff2') format('woff2'), + url('#{$fa-font-path}/fa-regular-400.ttf') format('truetype'); +} + +.far, +.#{$fa-css-prefix}-regular { + font-weight: 400; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/solid.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/solid.scss new file mode 100644 index 0000000..3dd635f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/solid.scss @@ -0,0 +1,26 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +@import 'functions'; +@import 'variables'; + +:root, :host { + --#{$fa-css-prefix}-style-family-classic: '#{ $fa-style-family }'; + --#{$fa-css-prefix}-font-solid: normal 900 1em/1 '#{ $fa-style-family }'; +} + +@font-face { + font-family: 'Font Awesome 6 Free'; + font-style: normal; + font-weight: 900; + font-display: $fa-font-display; + src: url('#{$fa-font-path}/fa-solid-900.woff2') format('woff2'), + url('#{$fa-font-path}/fa-solid-900.ttf') format('truetype'); +} + +.fas, +.#{$fa-css-prefix}-solid { + font-weight: 900; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/v4-shims.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/v4-shims.scss new file mode 100644 index 0000000..7893e7c --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/scss/vendors/fontawesome-free/v4-shims.scss @@ -0,0 +1,11 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +// V4 shims compile (Web Fonts-based) +// ------------------------- + +@import 'functions'; +@import 'variables'; +@import 'shims'; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/styles.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/styles.scss new file mode 100644 index 0000000..b625805 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/src/styles.scss @@ -0,0 +1,14 @@ +@import 'scss/base/typography'; + +@import 'scss/vendors/fontawesome-free/fontawesome.scss'; +@import 'scss/vendors/fontawesome-free/brands.scss'; +@import 'scss/vendors/fontawesome-free/regular.scss'; +@import 'scss/vendors/fontawesome-free/solid.scss'; + +@import 'scss/vendors/material_io'; + +@import 'scss/shame'; + + +html, body { height: 100%; } +body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/test_and_report.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/test_and_report.bash new file mode 100644 index 0000000..5ba4c0d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/test_and_report.bash @@ -0,0 +1,3 @@ +#!/bin/bash + +ng test --no-watch --code-coverage --browsers Firefox diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/tsconfig.app.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/tsconfig.app.json new file mode 100644 index 0000000..7dc7284 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/tsconfig.app.json @@ -0,0 +1,18 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [ + "node" + ] + }, + "files": [ + "src/main.ts", + "src/main.server.ts", + "server.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/tsconfig.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/tsconfig.json new file mode 100644 index 0000000..678336b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/tsconfig.json @@ -0,0 +1,32 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "sourceMap": true, + "declaration": false, + "experimentalDecorators": true, + "moduleResolution": "node", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "useDefineForClassFields": false, + "lib": [ + "ES2022", + "dom" + ] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/tsconfig.spec.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/tsconfig.spec.json new file mode 100644 index 0000000..be7e9da --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_readonly/readonly/readonly-frontend/tsconfig.spec.json @@ -0,0 +1,14 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/.gitignore b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/.gitignore new file mode 100644 index 0000000..839bdac --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/.gitignore @@ -0,0 +1,7 @@ +.env + +**/ca.key +**/ca.crt +**/ca.srl + +nginx/cert \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/ca-certificates/type b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/ca-certificates/type new file mode 100644 index 0000000..54619ed --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/ca-certificates/type @@ -0,0 +1 @@ +ca-certificates \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/docker-compose.yaml b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/docker-compose.yaml new file mode 100644 index 0000000..bf1a136 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/docker-compose.yaml @@ -0,0 +1,158 @@ +version: "2.4" + +services: + + ory-hydra-oauth2-example-client-write-and-read-backend: + image: chistousov/ory-hydra-oauth2-example-client-write-and-read-backend:1.0.0 + container_name: ory-hydra-oauth2-example-client-write-and-read-backend + environment: + - LANG=en_US.UTF-8 + - LANGUAGE=en_US:en + + - BPL_JAVA_NMT_LEVEL=detail + + # ca add + - SERVICE_BINDING_ROOT=/bindings + + # health check + - THC_PATH=/actuator/health + - THC_PORT=8080 + + - JAVA_TOOL_OPTIONS= + -Dspring.profiles.active=prod + + -Dspring.security.oauth2.client.registration.client-write-and-read.client-id=${CLIENT_WRITE_AND_READ_CLIENT_ID} + -Dspring.security.oauth2.client.registration.client-write-and-read.client-secret=${CLIENT_WRITE_AND_READ_CLIENT_SECRET} + -Dspring.security.oauth2.client.registration.client-write-and-read.redirect-uri=https://${DNS_CLIENT_WRITE_AND_READ}/api/login/oauth2/code/client-write-and-read + -Dspring.security.oauth2.client.registration.client-write-and-read.scope="read,write" + + -Dspring.security.oauth2.client.provider.spring.authorization-uri=https://${DNS_AUTHORIZATION_SERVER}/oauth2/auth + -Dspring.security.oauth2.client.provider.spring.token-uri=https://${DNS_AUTHORIZATION_SERVER}/oauth2/token + -Dspring.security.oauth2.client.provider.spring.user-info-uri=https://${DNS_AUTHORIZATION_SERVER}/userinfo + -Dspring.security.oauth2.client.provider.spring.user-name-attribute=sub + + -Dapplication.frontend.error-oauth2-page="/error" + -Dapplication.resource-server="https://${DNS_RESOURCE_SERVER}/" + + user: "1005" + mem_limit: 1G + logging: + options: + max-size: "100m" + max-file: "1" + volumes: + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + - type: bind + source: ca-certificates + target: /bindings/ca-certificates + read_only: true + healthcheck: + test: "/cnb/process/health-check" + interval: 20s + timeout: 5s + retries: 5 + start_period: 40s + networks: + - app-network + extra_hosts: + - "${DNS_AUTHORIZATION_SERVER}:${IP_AUTHORIZATION_SERVER}" + - "${DNS_RESOURCE_SERVER}:${IP_RESOURCE_SERVER}" + + ory-hydra-oauth2-example-client-write-and-read-frontend: + image: chistousov/ory-hydra-oauth2-example-client-write-and-read-frontend:1.0.0 + container_name: ory-hydra-oauth2-example-client-write-and-read-frontend + environment: + # health check + - THC_PATH=/health + - THC_PORT=4000 + + user: "1005" + mem_limit: 512M + logging: + options: + max-size: "100m" + max-file: "1" + volumes: + + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + healthcheck: + test: "/cnb/process/health-check" + interval: 20s + timeout: 5s + retries: 5 + start_period: 40s + depends_on: + ory-hydra-oauth2-example-client-write-and-read-backend: + condition: service_healthy + networks: + - app-network + + ory-hydra-oauth2-example-client-write-and-read-gateway-nginx: + image: nginx:1.25.3-bookworm + container_name: ory-hydra-oauth2-example-client-write-and-read-gateway-nginx + healthcheck: + test: '[ -e /var/run/nginx.pid ] || exit 1' + interval: 20s + timeout: 5s + retries: 5 + start_period: 40s + ports: + - 443:443 + volumes: + - type: bind + source: nginx/nginx.conf + target: /etc/nginx/nginx.conf + read_only: true + + - type: bind + source: nginx/confs + target: /etc/nginx/conf.d + read_only: true + + - type: bind + source: nginx/cert + target: /cert + read_only: true + + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + + restart: unless-stopped + depends_on: + ory-hydra-oauth2-example-client-write-and-read-backend: + condition: service_healthy + ory-hydra-oauth2-example-client-write-and-read-frontend: + condition: service_healthy + logging: + options: + max-size: "100m" + max-file: "1" + mem_limit: 1G + networks: + - app-network + + +networks: + app-network: diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/nginx/confs/client-write-and-read.conf.templ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/nginx/confs/client-write-and-read.conf.templ new file mode 100644 index 0000000..5a194aa --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/nginx/confs/client-write-and-read.conf.templ @@ -0,0 +1,70 @@ +server { + listen 443 ssl; + http2 on; + server_name ${DNS_CLIENT_WRITE_AND_READ}; + + ssl_certificate /cert/${DNS_CLIENT_WRITE_AND_READ}/${DNS_CLIENT_WRITE_AND_READ}.crt; + ssl_certificate_key /cert/${DNS_CLIENT_WRITE_AND_READ}/${DNS_CLIENT_WRITE_AND_READ}.key; + + add_header Strict-Transport-Security "max-age=31536000; preload" always; + add_header X-Content-Type-Options "nosniff"; + add_header X-Frame-Options "DENY"; + add_header X-XSS-Protection "1; mode=block"; + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + # Docker DNS + resolver 127.0.0.11; + + location ~ ^/(oauth2)/? { + set $ory_hydra_oauth2_example_client_write_and_read_backend2 http://ory-hydra-oauth2-example-client-write-and-read-backend:8080; + rewrite /(.*) /$1 break; + proxy_pass $ory_hydra_oauth2_example_client_write_and_read_backend2; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto-Version $http2; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /; + } + + + location /api/ { + set $ory_hydra_oauth2_example_client_write_and_read_backend http://ory-hydra-oauth2-example-client-write-and-read-backend:8080; + rewrite /api/(.*) /$1 break; + proxy_pass $ory_hydra_oauth2_example_client_write_and_read_backend; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto-Version $http2; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /api; + + } + + location / { + set $ory_hydra_oauth2_example_client_readonly_frontend http://ory-hydra-oauth2-example-client-write-and-read-frontend:4000; + rewrite /(.*) /$1 break; + proxy_pass $ory_hydra_oauth2_example_client_readonly_frontend; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto-Version $http2; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /; + + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/nginx/confs/default.conf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/nginx/confs/default.conf new file mode 100644 index 0000000..86124f6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/nginx/confs/default.conf @@ -0,0 +1,11 @@ +server { + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/nginx/nginx.conf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/nginx/nginx.conf new file mode 100644 index 0000000..2238388 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/nginx/nginx.conf @@ -0,0 +1,37 @@ + +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + variables_hash_bucket_size 512; + + proxy_buffer_size 128k; + proxy_buffers 4 256k; + proxy_busy_buffers_size 256k; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/.editorconfig b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/.editorconfig new file mode 100644 index 0000000..59d9a3a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/.gitignore b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/build.gradle b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/build.gradle new file mode 100644 index 0000000..2d79d14 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/build.gradle @@ -0,0 +1,95 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '2.7.14' + id 'io.spring.dependency-management' version '1.0.15.RELEASE' + id 'jacoco' +} + +ext { + httpproxy = "$System.env.HTTP_PROXY" ?: "" + httpsproxy = "$System.env.HTTPS_PROXY" ?: "" + noproxy = "$System.env.NO_PROXY" ?: "" + + repoImage = "$System.env.REPO_IMAGE" ?: "" + projectName = "$System.env.PROJECT_NAME" ?: "" + versionProj = "$System.env.VERSION" ?: "" + + set('testcontainersVersion', "1.18.3") +} + +group = 'com.github.chistousov' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '11' + withSourcesJar() +} + +compileJava.options.encoding = "UTF-8" +compileTestJava.options.encoding = "UTF-8" + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +jacocoTestReport { + dependsOn test + reports { + xml.required = true + } +} + +repositories { + mavenCentral() +} + +bootBuildImage { + imageName = "${repoImage}/${projectName}:${versionProj}" + environment = [ + // "HTTP_PROXY" : httpproxy.toString(), + // "HTTPS_PROXY" : httpsproxy.toString(), + // "NO_PROXY" : noproxy.toString(), + // add health check for docker + "BP_HEALTH_CHECKER_ENABLED": "true" + ] + buildpacks = ["urn:cnb:builder:paketo-buildpacks/java", "gcr.io/paketo-buildpacks/health-checker:latest"] +} + + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-actuator' + + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' + + implementation 'org.springframework.boot:spring-boot-starter-security' + + implementation 'org.springframework.boot:spring-boot-starter-webflux' + + implementation 'org.springframework.session:spring-session-core' + + compileOnly 'org.projectlombok:lombok' + + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' + annotationProcessor 'org.projectlombok:lombok' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'io.projectreactor:reactor-test' + testImplementation 'org.springframework.security:spring-security-test' + testImplementation 'org.testcontainers:junit-jupiter' + + testImplementation "org.testcontainers:mockserver:1.18.3" + testImplementation "org.mock-server:mockserver-client-java:5.15.0" +} + +dependencyManagement { + imports { + mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}" + } +} + +tasks.named('test') { + useJUnitPlatform() + finalizedBy jacocoTestReport +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/build_image.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/build_image.bash new file mode 100644 index 0000000..20115c8 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/build_image.bash @@ -0,0 +1,20 @@ +#!/bin/bash + +# export HTTP_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +# export HTTPS_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +# export NO_PROXY="localhost,127.0.0.1" + +export REPO_IMAGE="chistousov" +export PROJECT_NAME="ory-hydra-oauth2-example-client-write-and-read-backend" +export VERSION="1.0.0" + +docker pull paketobuildpacks/builder-jammy-full:0.3.316 + +./gradlew clean test +./gradlew bootBuildImage --builder=paketobuildpacks/builder-jammy-full:0.3.316 + +# publish in docker hub +# docker login +# docker push $REPO_IMAGE/$PROJECT_NAME:$VERSION +# docker logout +# docker rmi $REPO_IMAGE/$PROJECT_NAME:$VERSION diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/gradle/wrapper/gradle-wrapper.jar b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..033e24c Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/gradle/wrapper/gradle-wrapper.jar differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/gradle/wrapper/gradle-wrapper.properties b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..9f4197d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/gradlew b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/gradlew new file mode 100644 index 0000000..fcb6fca --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/gradlew.bat b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/lombok.config b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/lombok.config new file mode 100644 index 0000000..8f7e8aa --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/lombok.config @@ -0,0 +1 @@ +lombok.addLombokGeneratedAnnotation = true \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/settings.gradle b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/settings.gradle new file mode 100644 index 0000000..173ac99 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'write-and-read-backend' diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/SpringSecurityConfiguration.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/SpringSecurityConfiguration.java new file mode 100644 index 0000000..da4b2c4 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/SpringSecurityConfiguration.java @@ -0,0 +1,98 @@ +package com.github.chistousov.write_and_read_backend; + +import java.util.Arrays; +import java.util.stream.Collectors; + +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.SecurityWebFiltersOrder; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; +import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestCustomizers; +import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction; +import org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver; +import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver; +import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository; +import org.springframework.security.web.server.SecurityWebFilterChain; +import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository; +import org.springframework.web.reactive.function.client.WebClient; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.github.chistousov.write_and_read_backend.security.auth.logout.success_handlers.CustomServerLogoutSuccessHandler; +import com.github.chistousov.write_and_read_backend.security.auth.oauth2.handlers.CustomServerAuthenticationFailureHandler; +import com.github.chistousov.write_and_read_backend.security.filters.AuthenticateFrontendWebFilter; +import com.github.chistousov.write_and_read_backend.security.filters.IsAuthenticatedCheckWebFilter; + +@EnableWebFluxSecurity +public class SpringSecurityConfiguration { + + private String pathFrontendErrorPage; + private String pathReauthenticationLocation; + + public SpringSecurityConfiguration(Environment env) { + + String clientId = env.getRequiredProperty( + "spring.security.oauth2.client.registration.client-write-and-read.client-id"); + + this.pathReauthenticationLocation = Arrays.asList( + "oauth2", + "authorization", + clientId) + .stream() + .collect(Collectors.joining("/")); + + this.pathFrontendErrorPage = env.getRequiredProperty( + "application.frontend.error-oauth2-page"); + + } + + @Bean + public SecurityWebFilterChain settingsSec(ServerHttpSecurity http, + ServerOAuth2AuthorizationRequestResolver resolver) throws JsonProcessingException { + return http.authorizeExchange( + ex -> ex.pathMatchers("/actuator/health") + .permitAll() + // все запросы должны быть аутентифицированы + .anyExchange() + .authenticated() + + ) + // CSRF для SPA приложений + .csrf(csrf -> csrf.csrfTokenRepository( + CookieServerCsrfTokenRepository.withHttpOnlyFalse())) + .oauth2Login() + .authorizationRequestResolver(resolver) + .authenticationFailureHandler( + new CustomServerAuthenticationFailureHandler(pathFrontendErrorPage, + pathReauthenticationLocation)) + .and() + // конфигурация logout + .logout() + .logoutSuccessHandler(new CustomServerLogoutSuccessHandler()) + .and() + .addFilterBefore(new IsAuthenticatedCheckWebFilter(), + SecurityWebFiltersOrder.HTTP_BASIC) + .addFilterBefore(new AuthenticateFrontendWebFilter(), + SecurityWebFiltersOrder.HTTP_BASIC) + .build(); + } + + @Bean + public ServerOAuth2AuthorizationRequestResolver pkceResolver(ReactiveClientRegistrationRepository repo) { + var resolver = new DefaultServerOAuth2AuthorizationRequestResolver(repo); + resolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce()); + return resolver; + } + + @Bean + WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations, + ServerOAuth2AuthorizedClientRepository authorizedClients) { + ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction( + clientRegistrations, + authorizedClients); + return WebClient.builder() + .filter(oauth) + .build(); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/WriteAndReadBackendApplication.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/WriteAndReadBackendApplication.java new file mode 100644 index 0000000..ae3ba1c --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/WriteAndReadBackendApplication.java @@ -0,0 +1,16 @@ +package com.github.chistousov.write_and_read_backend; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.github.chistousov.write_and_read_backend.jacoco_ignore.ExcludeFromJacocoGeneratedReport; + +@SpringBootApplication +@ExcludeFromJacocoGeneratedReport +public class WriteAndReadBackendApplication { + + public static void main(String[] args) { + SpringApplication.run(WriteAndReadBackendApplication.class, args); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/controllers/CalculateController.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/controllers/CalculateController.java new file mode 100644 index 0000000..968fc67 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/controllers/CalculateController.java @@ -0,0 +1,57 @@ +package com.github.chistousov.write_and_read_backend.controllers; + +import java.net.URI; + +import org.springframework.core.env.Environment; +import org.springframework.http.ResponseEntity; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.server.WebSession; +import org.springframework.web.util.UriComponentsBuilder; + +import reactor.core.publisher.Mono; + +@Controller +@RequestMapping("/change") +public class CalculateController { + + private WebClient webClient; + private URI locationResourceServer; + + private URI locationCalculate; + + public CalculateController(WebClient webClient, Environment env) { + this.webClient = webClient; + this.locationResourceServer = URI.create( + env.getRequiredProperty( + "application.resource-server")); + + this.locationCalculate = UriComponentsBuilder + .fromUri(locationResourceServer) + .pathSegment("api", "calculate") + .build() + .toUri(); + } + + @PutMapping + public Mono> calculate( + @RegisteredOAuth2AuthorizedClient("client-write-and-read") OAuth2AuthorizedClient authorizedClient, + WebSession webSession) { + + return Mono.just( + ResponseEntity.ok(webClient.put() + .uri(locationCalculate) + .attributes( + ServerOAuth2AuthorizedClientExchangeFilterFunction + .oauth2AuthorizedClient( + authorizedClient)) + .retrieve() + .bodyToMono(Object.class))); + + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/controllers/GlobalExceptionHandler.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/controllers/GlobalExceptionHandler.java new file mode 100644 index 0000000..9ead387 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/controllers/GlobalExceptionHandler.java @@ -0,0 +1,71 @@ +package com.github.chistousov.write_and_read_backend.controllers; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.support.WebExchangeBindException; + +import com.github.chistousov.write_and_read_backend.jacoco_ignore.ExcludeFromJacocoGeneratedReport; + +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@ControllerAdvice +@Slf4j +@ExcludeFromJacocoGeneratedReport +public class GlobalExceptionHandler { + + private static final String TEXT_PLAIN_CHARSET_UTF_8 = "text/plain;charset=utf-8"; + private static final String CONTENT_TYPE = "Content-Type"; + + // invalid model handler + @ExceptionHandler(WebExchangeBindException.class) + public ResponseEntity> handleException(WebExchangeBindException e) { + var errors = e.getBindingResult() + .getAllErrors() + .stream() + .map(DefaultMessageSourceResolvable::getDefaultMessage) + .collect(Collectors.toList()); + return ResponseEntity.badRequest().body(errors); + + } + + @ExceptionHandler(IllegalArgumentException.class) + public Mono illegalArgumentPerfom(ServerHttpRequest req, ServerHttpResponse response, + IllegalArgumentException ex) { + + response.getHeaders().set(CONTENT_TYPE, TEXT_PLAIN_CHARSET_UTF_8); + response.setStatusCode(HttpStatus.BAD_REQUEST); + + byte[] bodyResponse = "Request received with incorrect data ".getBytes(StandardCharsets.UTF_8); + DataBuffer buffer = response.bufferFactory().wrap(bodyResponse); + + return response.writeWith(Flux.just(buffer)) + .doOnEach(el -> log.error(req.getPath().toString() + " ", ex)); + + } + + @ExceptionHandler(Exception.class) + public Mono commonHandler(ServerHttpRequest req, ServerHttpResponse response, Exception ex) { + + response.getHeaders().set(CONTENT_TYPE, TEXT_PLAIN_CHARSET_UTF_8); + response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + + byte[] bodyResponse = "Error in server ".getBytes(StandardCharsets.UTF_8); + DataBuffer buffer = response.bufferFactory().wrap(bodyResponse); + + return response.writeWith(Flux.just(buffer)) + .doOnEach(el -> log.error(req.getPath().toString() + " ", ex)); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/jacoco_ignore/ExcludeFromJacocoGeneratedReport.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/jacoco_ignore/ExcludeFromJacocoGeneratedReport.java new file mode 100644 index 0000000..320f032 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/jacoco_ignore/ExcludeFromJacocoGeneratedReport.java @@ -0,0 +1,21 @@ +package com.github.chistousov.write_and_read_backend.jacoco_ignore; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marking with this annotation, we ignore code coverage by JaCoCo tests for a + * class or method. + * Помечая данной аннотацией, мы игнорируем для класса или метода покрытие кода + * тестами JaCoCo + */ +@Documented +@Retention(RUNTIME) +@Target({ TYPE, METHOD }) +public @interface ExcludeFromJacocoGeneratedReport { +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/security/auth/logout/success_handlers/CustomServerLogoutSuccessHandler.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/security/auth/logout/success_handlers/CustomServerLogoutSuccessHandler.java new file mode 100644 index 0000000..7e6b265 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/security/auth/logout/success_handlers/CustomServerLogoutSuccessHandler.java @@ -0,0 +1,23 @@ +package com.github.chistousov.write_and_read_backend.security.auth.logout.success_handlers; + +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.server.WebFilterExchange; +import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler; + +import reactor.core.publisher.Mono; + +public class CustomServerLogoutSuccessHandler implements ServerLogoutSuccessHandler { + + private static final HttpStatus httpStatus = HttpStatus.NO_CONTENT; + + @Override + public Mono onLogoutSuccess(WebFilterExchange webFilterExchange, Authentication authentication) { + return Mono.fromRunnable(() -> { + ServerHttpResponse response = webFilterExchange.getExchange().getResponse(); + response.setStatusCode(httpStatus); + }); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/security/auth/oauth2/handlers/CustomServerAuthenticationFailureHandler.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/security/auth/oauth2/handlers/CustomServerAuthenticationFailureHandler.java new file mode 100644 index 0000000..9b1263b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/security/auth/oauth2/handlers/CustomServerAuthenticationFailureHandler.java @@ -0,0 +1,81 @@ +package com.github.chistousov.write_and_read_backend.security.auth.oauth2.handlers; + +import java.net.URI; +import java.util.List; + +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.server.WebFilterExchange; +import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler; +import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.util.UriComponentsBuilder; + +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; + +@Slf4j +public class CustomServerAuthenticationFailureHandler implements ServerAuthenticationFailureHandler { + + private static final HttpStatus httpStatus = HttpStatus.FOUND; + + private static final String ERROR = "error"; + private static final String ERROR_DESCRIPTION = "error_description"; + private static final String REAUTHENTICATION_LOCATION = "reauthentication_location"; + + private static final String DEFAULT_ERROR = "server_error"; + private static final String DEFAULT_ERROR_DESCRIPTION = "Неизвестная ошибка сервера"; + + private URI location; + private String reauthenticationLocation; + + public CustomServerAuthenticationFailureHandler(String location, String reauthenticationLocation) { + Assert.notNull(location, "location cannot be null"); + Assert.notNull(reauthenticationLocation, "reauthenticationLocation cannot be null"); + this.location = URI.create(location); + this.reauthenticationLocation = reauthenticationLocation; + } + + @Override + public Mono onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception) { + + // что оправиться с редиректом + String queryParamError = null; + String queryParamErrorDescription = null; + + // достаем необходимые параметры запроса + MultiValueMap queryParamsRequest = webFilterExchange.getExchange().getRequest().getQueryParams(); + var queryParamsError = queryParamsRequest.getOrDefault(ERROR, List.of(DEFAULT_ERROR)); + var queryParamsErrorDescription = queryParamsRequest.getOrDefault(ERROR_DESCRIPTION, + List.of(DEFAULT_ERROR_DESCRIPTION)); + + queryParamError = queryParamsError.get(0); + + queryParamErrorDescription = queryParamsErrorDescription.get(0); + + // формируем необходимые параметры запроса для редиректа + MultiValueMap queryParamsForRedirect = new LinkedMultiValueMap<>(); + queryParamsForRedirect.add(ERROR, queryParamError); + queryParamsForRedirect.add(ERROR_DESCRIPTION, queryParamErrorDescription); + queryParamsForRedirect.add(REAUTHENTICATION_LOCATION, reauthenticationLocation); + + URI redirectToFrontendLocation = UriComponentsBuilder + .fromUri(location) + .queryParams(queryParamsForRedirect) + .build() + .toUri(); + + return Mono.fromRunnable(() -> { + ServerHttpResponse response = webFilterExchange.getExchange().getResponse(); + response.setStatusCode(httpStatus); + + log.debug("Redirecting to '{}'", redirectToFrontendLocation.toASCIIString()); + + response.getHeaders().setLocation(redirectToFrontendLocation); + + }); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/security/filters/AuthenticateFrontendWebFilter.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/security/filters/AuthenticateFrontendWebFilter.java new file mode 100644 index 0000000..c5b36ad --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/security/filters/AuthenticateFrontendWebFilter.java @@ -0,0 +1,57 @@ +package com.github.chistousov.write_and_read_backend.security.filters; + +import java.net.URI; + +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; + +import reactor.core.publisher.Mono; + +public class AuthenticateFrontendWebFilter implements WebFilter { + + private static final String AUTHENTICATION_FRONTEND = "/authenticate-frontend"; + + private ServerWebExchangeMatcher requiresAuthenticateFrontendMatcher = ServerWebExchangeMatchers + .pathMatchers(HttpMethod.GET, AUTHENTICATION_FRONTEND); + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + // endpoint, который если пользователь аутентифицирован всегда возвращает / + + // соответствует URI /authenticate-frontend + return this.requiresAuthenticateFrontendMatcher.matches(exchange) + .filter(MatchResult::isMatch) + // request не соответствует /authenticate-frontend, тогда пропускаем + .switchIfEmpty(chain.filter(exchange).then(Mono.empty())) + // есть ли контекст безопасности? + .flatMap(matchResult -> ReactiveSecurityContextHolder + .getContext() + // нету констекста безопасности значит создаем фиктивный объект + .switchIfEmpty(Mono.just(new SecurityContextImpl()))) + .flatMap(securityContext -> { + Authentication authentication = securityContext.getAuthentication(); + // если контекст безопасности и пользователь аутентифицирован + if (authentication != null && authentication.isAuthenticated()) { + + return Mono.fromRunnable(() -> { + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(HttpStatus.FOUND); + + response.getHeaders().setLocation(URI.create("/")); + }); + } + // иначе обрабатываем дальше по фильтрам запрос + return chain.filter(exchange); + }); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/security/filters/IsAuthenticatedCheckWebFilter.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/security/filters/IsAuthenticatedCheckWebFilter.java new file mode 100644 index 0000000..ac06b3a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/java/com/github/chistousov/write_and_read_backend/security/filters/IsAuthenticatedCheckWebFilter.java @@ -0,0 +1,85 @@ +package com.github.chistousov.write_and_read_backend.security.filters; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import reactor.core.publisher.Mono; + +public class IsAuthenticatedCheckWebFilter implements WebFilter { + + private static final String AUTHENTICATION_FRONTEND = "/authenticate-frontend"; + + private ServerWebExchangeMatcher requiresLoggedInMatcher = ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, + "/logged-in"); + + private String json; + + public IsAuthenticatedCheckWebFilter() throws JsonProcessingException { + Map payload = new HashMap<>(); + payload.put("redirect_to", AUTHENTICATION_FRONTEND); + + json = new ObjectMapper().writeValueAsString(payload); + } + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + // проверяем есть аутентификация и авторизация + + // соответствует URI /logged-in + return this.requiresLoggedInMatcher.matches(exchange) + .filter(MatchResult::isMatch) + // request не соответствует /logged-in, тогда пропускаем + .switchIfEmpty(chain.filter(exchange).then(Mono.empty())) + // есть ли контекст безопасности? + .flatMap(matchResult -> ReactiveSecurityContextHolder + .getContext() + // нету констекста безопасности значит создаем фиктивный объект + .switchIfEmpty(Mono.just(new SecurityContextImpl()))) + .flatMap(securityContext -> { + Authentication authentication = securityContext.getAuthentication(); + // нету констекста безопасности или не аутентифицирован + if (authentication == null || !authentication.isAuthenticated()) { + + return Mono.defer(() -> { + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(HttpStatus.UNAUTHORIZED); + response.getHeaders().setContentType(MediaType.TEXT_PLAIN); + + DataBuffer dataBuffer = response.bufferFactory() + .wrap(json.getBytes(StandardCharsets.UTF_8)); + + return response.writeAndFlushWith(Mono.just(Mono.just(dataBuffer))) + .doOnError(ex -> DataBufferUtils.release(dataBuffer)); + }); + } + + // иначе 204 - все хорошо! + return Mono.fromRunnable(() -> { + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(HttpStatus.NO_CONTENT); + response.getHeaders().setContentType(MediaType.TEXT_PLAIN); + }); + }); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000..ee8583f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,5 @@ +{"properties": [{ + "name": "application.title", + "type": "java.lang.String", + "description": "title this app" +}]} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/resources/application-prod.yml b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/resources/application-prod.yml new file mode 100644 index 0000000..c56eee6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/resources/application-prod.yml @@ -0,0 +1,3 @@ +logging: + level: + root: info \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/resources/application.yml b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/resources/application.yml new file mode 100644 index 0000000..351dcea --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/resources/application.yml @@ -0,0 +1,32 @@ +application: + title: OAuth 2.0 Client Write And Read Example Ory Hydra + +spring: + + output: + ansi: + enabled: always + + security: + oauth2: + client: + registration: + client-write-and-read: + provider: spring + client-authentication-method: client_secret_basic + authorization-grant-type: authorization_code + +logging: + level: + root: debug + + +server: + forward-headers-strategy: framework + reactive: + session: + cookie: + name: SESSION-CLIENT-WRITE-AND-READ + error: + include-stacktrace: never + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/resources/banner.txt b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/resources/banner.txt new file mode 100644 index 0000000..a4e1230 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/main/resources/banner.txt @@ -0,0 +1,7 @@ +${AnsiColor.BRIGHT_CYAN} +,---.| o | +| |---..,---.|--- ,---.. .,---.,---.. , +| | ||`---.| | || |`---.| | \ / +`---'` '``---'`---'`---'`---'`---'`---' `' +${AnsiBackground.WHITE}${AnsiColor.BRIGHT_BLACK}${application.title} +Powered by Spring Boot ${spring-boot.version}${AnsiColor.DEFAULT}${AnsiBackground.DEFAULT} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/test/java/com/github/chistousov/write_and_read_backend/controllers/CalculateControllerTest.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/test/java/com/github/chistousov/write_and_read_backend/controllers/CalculateControllerTest.java new file mode 100644 index 0000000..bd6e05c --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/test/java/com/github/chistousov/write_and_read_backend/controllers/CalculateControllerTest.java @@ -0,0 +1,580 @@ +package com.github.chistousov.write_and_read_backend.controllers; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Random; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockserver.client.MockServerClient; +import org.mockserver.file.FileReader; +import org.mockserver.model.Header; +import org.mockserver.model.OpenAPIDefinition; +import org.mockserver.verify.VerificationTimes; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.context.annotation.Import; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.util.UriComponentsBuilder; +import org.testcontainers.containers.MockServerContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.shaded.com.google.common.collect.ImmutableMap; +import org.testcontainers.utility.DockerImageName; + +import com.github.chistousov.write_and_read_backend.SpringSecurityConfiguration; + +import static org.mockserver.mock.OpenAPIExpectation.openAPIExpectation; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; +import static org.mockserver.model.JsonBody.json; + +@WebFluxTest +@Import(SpringSecurityConfiguration.class) +@Testcontainers +public class CalculateControllerTest { + + private static final String SESSION_CLIENT = "SESSION"; + + private static final DockerImageName MOCKSERVER_IMAGE = DockerImageName + .parse("mockserver/mockserver") + .withTag("mockserver-" + MockServerClient.class.getPackage().getImplementationVersion()); + + @Container + public static MockServerContainer oryHydraMockServer = new MockServerContainer(MOCKSERVER_IMAGE); + private static MockServerClient oryHydraMockServerClient; + + private static final String clientId = "someUser"; + private static final String clientSecret = "someSecret"; + + private static String authorizationUri; + private static String redirectUri; + + @Container + public static MockServerContainer resourceServerMockServer = new MockServerContainer(MOCKSERVER_IMAGE); + private static MockServerClient resourceServerMockServerClient; + + @Autowired + private WebTestClient thisServerWebTestClient; + + private static WebTestClient oryHydraWebTestClient; + + @DynamicPropertySource + public static void settings(DynamicPropertyRegistry registry) + throws UnsupportedEncodingException { + + String baseHostPath = "http://localhost:8080"; + + oryHydraMockServerClient = new MockServerClient(oryHydraMockServer.getHost(), + oryHydraMockServer.getServerPort()); + + String baseOryHydraPath = String.format("http://%s:%d", oryHydraMockServer.getHost(), + oryHydraMockServer.getServerPort()); + + resourceServerMockServerClient = new MockServerClient(resourceServerMockServer.getHost(), + resourceServerMockServer.getServerPort()); + + String baseResourceServerPath = String.format("http://%s:%d", resourceServerMockServer.getHost(), + resourceServerMockServer.getServerPort()); + + registry.add("spring.security.oauth2.client.registration.client-write-and-read.client-id", () -> clientId); + registry.add("spring.security.oauth2.client.registration.client-write-and-read.client-secret", + () -> clientSecret); + + redirectUri = UriComponentsBuilder + .fromUri(URI.create(baseHostPath)) + .pathSegment("login", "oauth2", "code", "client-write-and-read") + .build() + .toUriString(); + + registry.add("spring.security.oauth2.client.registration.client-write-and-read.redirect-uri", + () -> redirectUri); + + authorizationUri = UriComponentsBuilder + .fromUri(URI.create(baseOryHydraPath)) + .pathSegment("oauth2", "auth") + .build() + .toUriString(); + + oryHydraWebTestClient = WebTestClient.bindToServer() + .baseUrl(baseOryHydraPath) + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .defaultHeader("X-Forwarded-Proto", "https") + .build(); + + registry.add("spring.security.oauth2.client.provider.spring.authorization-uri", () -> authorizationUri); + registry.add("spring.security.oauth2.client.provider.spring.token-uri", () -> UriComponentsBuilder + .fromUri(URI.create(baseOryHydraPath)) + .pathSegment("oauth2", "token") + .build() + .toUriString()); + registry.add("spring.security.oauth2.client.provider.spring.user-info-uri", () -> UriComponentsBuilder + .fromUri(URI.create(baseOryHydraPath)) + .pathSegment("userinfo") + .build() + .toUriString()); + registry.add("spring.security.oauth2.client.provider.spring.user-name-attribute", () -> "sub"); + + registry.add("application.frontend.error-oauth2-page", () -> "/error"); + + registry.add("application.resource-server", () -> baseResourceServerPath); + + registry.add("server.reactive.session.cookie.name", () -> SESSION_CLIENT); + } + + @BeforeEach + void setUp() { + oryHydraMockServerClient.reset(); + resourceServerMockServerClient.reset(); + + thisServerWebTestClient = thisServerWebTestClient.mutate() + .responseTimeout(Duration.ofMinutes(5)) + .build(); + } + + String authLocation; + String cookieThisSession; + + @Test + @DisplayName("/change is success") + void testCalculate() { + // given (instead of when) + + oryHydraMockServerClient + .upsert( + openAPIExpectation( + FileReader.readFileFromClassPathOrPath( + "src/test/resources/ory-hydra-open-api-v3.json"), + ImmutableMap.of( + "oAuth2Authorize", "302", + "getOidcUserInfo", "200"))); + + oryHydraMockServerClient + .when( + new OpenAPIDefinition() + .withSpecUrlOrPayload( + FileReader.readFileFromClassPathOrPath( + "src/test/resources/ory-hydra-open-api-v3.json")) + .withOperationId("oauth2TokenExchange")) + .respond( + response() + .withStatusCode(200) + .withHeaders(new Header("Content-Type", "application/json; charset=utf-8")) + .withBody( + json( + "{\n" + + "\"access_token\": \"gAanWA6Hym67rOq5k0ZGUWp3dR_fxILn3cyRj4hPt-c.wVdASIZBWL1XjHTz-3UImRkpTJLMbFwCMkPeOIh_sSM\",\n" + + + "\"expires_in\": 8888,\n" + + "\"id_token\": 68678,\n" + + "\"refresh_token\": \"gAanWA6Hym67rOq5k0ZGUWp3dR_fxILn3cyRj4hPt-c.wVdASIZBWL1XjHTz-3UImRkpTJLMbFwCMkPeOIh_sSM\",\n" + + + "\"scope\": \"read write\",\n" + + "\"token_type\": \"bearer\"\n" + + "}"))); + + resourceServerMockServerClient + .upsert( + openAPIExpectation( + FileReader.readFileFromClassPathOrPath( + "src/test/resources/resource-server-open-api-v3.json"), + ImmutableMap.of( + "calculate", "200" + + ))); + + // when + + // проверка аутентифицирован(и авторизован) ли пользователь без сессии + thisServerWebTestClient + .get() + .uri("/logged-in") + .exchange() + .expectStatus() + .isUnauthorized() + .expectBody() + .jsonPath("$").isNotEmpty() + .jsonPath("$").isMap() + .jsonPath("$.length()").isEqualTo(1) + .jsonPath("$.redirect_to").isNotEmpty() + .jsonPath("$.redirect_to").isEqualTo("/authenticate-frontend"); + + // аутентифицируемся + thisServerWebTestClient + .get() + .uri("/authenticate-frontend") + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", "/oauth2/authorization/client-write-and-read"); + + thisServerWebTestClient + .get() + .uri("/oauth2/authorization/client-write-and-read") + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", String.format( + "^http:\\/\\/%s:%d\\/oauth2\\/auth\\?response_type=code&client_id=%s&state=.+&redirect_uri=%s&code_challenge=.+&code_challenge_method=S256$", + oryHydraMockServer.getHost(), + oryHydraMockServer.getServerPort(), + clientId, + redirectUri)) + .expectHeader() + .value("Location", location -> authLocation = location) + .expectCookie() + .exists(SESSION_CLIENT) + .expectCookie() + .value(SESSION_CLIENT, val -> cookieThisSession = val); + + URI authLocationURI = URI.create(authLocation); + var queryParamsStrAuthLocationURI = authLocationURI.getQuery(); + + var queryParamsAuthLocationURI = Arrays + .stream(queryParamsStrAuthLocationURI.split("&")) + .map(this::splitQueryParameter) + .collect( + Collectors.groupingBy( + SimpleImmutableEntry::getKey, + LinkedHashMap::new, + Collectors.mapping(Map.Entry::getValue, + Collectors.toList()))); + + String state = queryParamsAuthLocationURI.get("state").get(0); + + oryHydraWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/oauth2/auth") + .query(queryParamsStrAuthLocationURI) + .build()) + .exchange() + .expectStatus() + .isFound(); + + // прошла аутентификация и авторизация ... Получаем code в redirect uri. + + String code = "someCode"; + + thisServerWebTestClient + .get() + .uri( + + uriBuilder -> uriBuilder + .path("/login/oauth2/code/client-write-and-read") + .queryParam("code", code) + .queryParam("state", state) + .build()) + .cookie(SESSION_CLIENT, cookieThisSession) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", "/") + .expectCookie() + .exists(SESSION_CLIENT) + .expectCookie() + .value(SESSION_CLIENT, val -> cookieThisSession = val); + + // Мы аутентифицированы и авторизованы + + // проверим еще раз + + thisServerWebTestClient + .get() + .uri("/logged-in") + .cookie(SESSION_CLIENT, cookieThisSession) + .exchange() + .expectStatus() + .isNoContent(); + + thisServerWebTestClient + .get() + .uri("/authenticate-frontend") + .cookie(SESSION_CLIENT, cookieThisSession) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", "/"); + + // получем данные с сессией (аутентифицирован и авторизован) + + String tokenCSRF = generateStr(); + + thisServerWebTestClient + .put() + .uri( + + uriBuilder -> uriBuilder + .path("/change") + .build()) + .cookie(SESSION_CLIENT, cookieThisSession) + .cookie("XSRF-TOKEN", tokenCSRF) + .header("X-XSRF-TOKEN", tokenCSRF) + .exchange() + .expectStatus() + .isOk(); + + // выход + + tokenCSRF = generateStr(); + + thisServerWebTestClient + .post() + .uri( + + uriBuilder -> uriBuilder + .path("/logout") + .build()) + .cookie("XSRF-TOKEN", tokenCSRF) + .header("X-XSRF-TOKEN", tokenCSRF) + .cookie(SESSION_CLIENT, cookieThisSession) + .exchange() + .expectStatus() + .isNoContent(); + + // then (instead of verify + + oryHydraMockServerClient.verify( + request() + .withMethod("GET") + .withPath("/oauth2/auth"), + VerificationTimes.exactly(1)); + + oryHydraMockServerClient.verify( + request() + .withMethod("POST") + .withPath("/oauth2/token"), + VerificationTimes.exactly(1)); + + oryHydraMockServerClient.verify( + request() + .withMethod("GET") + .withPath("/userinfo"), + VerificationTimes.exactly(1)); + + resourceServerMockServerClient.verify( + request() + .withMethod("PUT") + .withPath("/api/calculate"), + VerificationTimes.exactly(1)); + } + + @Test + @DisplayName("/change is fauil. Token is incorrect") + void testCalculateTokenIncorrect() { + // given (instead of when) + + oryHydraMockServerClient + .upsert( + openAPIExpectation( + FileReader.readFileFromClassPathOrPath( + "src/test/resources/ory-hydra-open-api-v3.json"), + ImmutableMap.of( + "oAuth2Authorize", "302", + "oauth2TokenExchange", "200"))); + + // when + + // проверка аутентифицирован(и авторизован) ли пользователь без сессии + thisServerWebTestClient + .get() + .uri("/logged-in") + .exchange() + .expectStatus() + .isUnauthorized() + .expectBody() + .jsonPath("$").isNotEmpty() + .jsonPath("$").isMap() + .jsonPath("$.length()").isEqualTo(1) + .jsonPath("$.redirect_to").isNotEmpty() + .jsonPath("$.redirect_to").isEqualTo("/authenticate-frontend"); + + // аутентифицируемся + thisServerWebTestClient + .get() + .uri("/authenticate-frontend") + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", "/oauth2/authorization/client-write-and-read"); + + thisServerWebTestClient + .get() + .uri("/oauth2/authorization/client-write-and-read") + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", String.format( + "^http:\\/\\/%s:%d\\/oauth2\\/auth\\?response_type=code&client_id=%s&state=.+&redirect_uri=%s&code_challenge=.+&code_challenge_method=S256$", + oryHydraMockServer.getHost(), + oryHydraMockServer.getServerPort(), + clientId, + redirectUri)) + .expectHeader() + .value("Location", location -> authLocation = location) + .expectCookie() + .exists(SESSION_CLIENT) + .expectCookie() + .value(SESSION_CLIENT, val -> cookieThisSession = val); + + URI authLocationURI = URI.create(authLocation); + var queryParamsStrAuthLocationURI = authLocationURI.getQuery(); + + var queryParamsAuthLocationURI = Arrays + .stream(queryParamsStrAuthLocationURI.split("&")) + .map(this::splitQueryParameter) + .collect( + Collectors.groupingBy( + SimpleImmutableEntry::getKey, + LinkedHashMap::new, + Collectors.mapping(Map.Entry::getValue, + Collectors.toList()))); + + String state = queryParamsAuthLocationURI.get("state").get(0); + + oryHydraWebTestClient + .get() + .uri( + uriBuilder -> uriBuilder + .path("/oauth2/auth") + .query(queryParamsStrAuthLocationURI) + .build()) + .exchange() + .expectStatus() + .isFound(); + + // прошла аутентификация и авторизация ... Получаем code в redirect uri. + + String code = "someCode"; + + thisServerWebTestClient + .get() + .uri( + + uriBuilder -> uriBuilder + .path("/login/oauth2/code/client-write-and-read") + .queryParam("code", code) + .queryParam("state", state) + .build()) + .cookie(SESSION_CLIENT, cookieThisSession) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", String.format( + "^\\/error\\?error=%s&error_description=%s&reauthentication_location=%s$", + "server_error", + URLEncoder.encode("Неизвестная ошибка сервера", StandardCharsets.UTF_8).replace("+", "%20"), + "oauth2\\/authorization\\/someUser")); + + // then (instead of verify + + oryHydraMockServerClient.verify( + request() + .withMethod("GET") + .withPath("/oauth2/auth"), + VerificationTimes.exactly(1)); + + oryHydraMockServerClient.verify( + request() + .withMethod("POST") + .withPath("/oauth2/token"), + VerificationTimes.exactly(1)); + + } + + @Test + @DisplayName("/oauth2/auth is faul.") + void fauilRequestToOryHydra() { + // given (instead of when) + + final String error = "invalid_request"; + final String errorDescription = "Очень серьезная ошибка"; + + // when + + thisServerWebTestClient + .get() + .uri( + + uriBuilder -> uriBuilder + .path("/login/oauth2/code/client-write-and-read") + .queryParam("error", error) + .queryParam("error_description", errorDescription) + .queryParam("state", "xyz") + .build()) + .exchange() + .expectStatus() + .isFound() + .expectHeader() + .exists("Location") + .expectHeader() + .valueMatches("Location", String.format( + "^\\/error\\?error=%s&error_description=%s&reauthentication_location=%s$", + error, + URLEncoder.encode(errorDescription, StandardCharsets.UTF_8).replace("+", "%20"), + "oauth2\\/authorization\\/someUser")); + + // then (instead of verify + + } + + // для парсинга QueryParameter URI + private SimpleImmutableEntry splitQueryParameter(String it) { + final int idx = it.indexOf("="); + final String key = idx > 0 ? it.substring(0, idx) : it; + final String value = idx > 0 && it.length() > idx + 1 ? it.substring(idx + 1) : null; + return new SimpleImmutableEntry<>( + URLDecoder.decode(key, StandardCharsets.UTF_8), + URLDecoder.decode(value, StandardCharsets.UTF_8)); + } + + private static String generateStr() { + // генерим случайную строки для state + int leftLimit = 97; // letter 'a' + int rightLimit = 122; // letter 'z' + int targetStringLength = 30; + Random random = new Random(); + StringBuilder buffer = new StringBuilder(targetStringLength); + for (int i = 0; i < targetStringLength; i++) { + int randomLimitedInt = leftLimit + (int) (random.nextFloat() * (rightLimit - leftLimit + 1)); + buffer.append((char) randomLimitedInt); + } + return buffer.toString(); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/test/resources/ory-hydra-open-api-v3.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/test/resources/ory-hydra-open-api-v3.json new file mode 100644 index 0000000..f812412 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/test/resources/ory-hydra-open-api-v3.json @@ -0,0 +1,3689 @@ +{ + "components": { + "responses": { + "emptyResponse": { + "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is\ntypically 201." + }, + "errorOAuth2BadRequest": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "Bad Request Error Response" + }, + "errorOAuth2Default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "Default Error Response" + }, + "errorOAuth2NotFound": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "Not Found Error Response" + }, + "listOAuth2Clients": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/oAuth2Client" + }, + "type": "array" + } + } + }, + "description": "Paginated OAuth2 Client List Response" + } + }, + "schemas": { + "DefaultError": {}, + "JSONRawMessage": { + "title": "JSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger." + }, + "NullBool": { + "nullable": true, + "type": "boolean" + }, + "NullDuration": { + "description": "Specify a time duration in milliseconds, seconds, minutes, hours.", + "pattern": "^([0-9]+(ns|us|ms|s|m|h))*$", + "title": "Time duration", + "type": "string" + }, + "NullInt": { + "nullable": true, + "type": "integer" + }, + "NullString": { + "nullable": true, + "type": "string" + }, + "NullTime": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "NullUUID": { + "format": "uuid4", + "nullable": true, + "type": "string" + }, + "StringSliceJSONFormat": { + "items": { + "type": "string" + }, + "title": "StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.", + "type": "array" + }, + "Time": { + "format": "date-time", + "type": "string" + }, + "UUID": { + "format": "uuid4", + "type": "string" + }, + "acceptOAuth2ConsentRequest": { + "properties": { + "grant_access_token_audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "grant_scope": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "handled_at": { + "$ref": "#/components/schemas/nullTime" + }, + "remember": { + "description": "Remember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same\nclient asks the same user for the same, or a subset of, scope.", + "type": "boolean" + }, + "remember_for": { + "description": "RememberFor sets how long the consent authorization should be remembered for in seconds. If set to `0`, the\nauthorization will be remembered indefinitely.", + "format": "int64", + "type": "integer" + }, + "session": { + "$ref": "#/components/schemas/acceptOAuth2ConsentRequestSession" + } + }, + "title": "The request payload used to accept a consent request.", + "type": "object" + }, + "acceptOAuth2ConsentRequestSession": { + "properties": { + "access_token": { + "description": "AccessToken sets session data for the access and refresh token, as well as any future tokens issued by the\nrefresh grant. Keep in mind that this data will be available to anyone performing OAuth 2.0 Challenge Introspection.\nIf only your services can perform OAuth 2.0 Challenge Introspection, this is usually fine. But if third parties\ncan access that endpoint as well, sensitive data from the session might be exposed to them. Use with care!" + }, + "id_token": { + "description": "IDToken sets session data for the OpenID Connect ID token. Keep in mind that the session'id payloads are readable\nby anyone that has access to the ID Challenge. Use with care!" + } + }, + "title": "Pass session data to a consent request.", + "type": "object" + }, + "acceptOAuth2LoginRequest": { + "properties": { + "acr": { + "description": "ACR sets the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it\nto express that, for example, a user authenticated using two factor authentication.", + "type": "string" + }, + "amr": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "context": { + "$ref": "#/components/schemas/JSONRawMessage" + }, + "extend_session_lifespan": { + "description": "Extend OAuth2 authentication session lifespan\n\nIf set to `true`, the OAuth2 authentication cookie lifespan is extended. This is for example useful if you want the user to be able to use `prompt=none` continuously.\n\nThis value can only be set to `true` if the user has an authentication, which is the case if the `skip` value is `true`.", + "type": "boolean" + }, + "force_subject_identifier": { + "description": "ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the\n(Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID\nConnect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client.\n\nPlease note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the\nsub claim in the OAuth 2.0 Introspection.\n\nPer default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself\nyou can use this field. Please note that setting this field has no effect if `pairwise` is not configured in\nORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's\nconfiguration).\n\nPlease also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies\nthat you have to compute this value on every authentication process (probably depending on the client ID or some\nother unique value).\n\nIf you fail to compute the proper value, then authentication processes which have id_token_hint set might fail.", + "type": "string" + }, + "remember": { + "description": "Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store\na cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she\nwill not be asked to log in again.", + "type": "boolean" + }, + "remember_for": { + "description": "RememberFor sets how long the authentication should be remembered for in seconds. If set to `0`, the\nauthorization will be remembered for the duration of the browser session (using a session cookie).", + "format": "int64", + "type": "integer" + }, + "subject": { + "description": "Subject is the user ID of the end-user that authenticated.", + "type": "string" + } + }, + "required": [ + "subject" + ], + "title": "HandledLoginRequest is the request payload used to accept a login request.", + "type": "object" + }, + "createJsonWebKeySet": { + "description": "Create JSON Web Key Set Request Body", + "properties": { + "alg": { + "description": "JSON Web Key Algorithm\n\nThe algorithm to be used for creating the key. Supports `RS256`, `ES256`, `ES512`, `HS512`, and `HS256`.", + "type": "string" + }, + "kid": { + "description": "JSON Web Key ID\n\nThe Key ID of the key to be created.", + "type": "string" + }, + "use": { + "description": "JSON Web Key Use\n\nThe \"use\" (public key use) parameter identifies the intended use of\nthe public key. The \"use\" parameter is employed to indicate whether\na public key is used for encrypting data or verifying the signature\non data. Valid values are \"enc\" and \"sig\".", + "type": "string" + } + }, + "required": [ + "alg", + "use", + "kid" + ], + "type": "object" + }, + "errorOAuth2": { + "description": "Error", + "properties": { + "error": { + "description": "Error", + "type": "string" + }, + "error_debug": { + "description": "Error Debug Information\n\nOnly available in dev mode.", + "type": "string" + }, + "error_description": { + "description": "Error Description", + "type": "string" + }, + "error_hint": { + "description": "Error Hint\n\nHelps the user identify the error cause.", + "example": "The redirect URL is not allowed.", + "type": "string" + }, + "status_code": { + "description": "HTTP Status Code", + "example": 401, + "format": "int64", + "type": "integer" + } + }, + "type": "object" + }, + "genericError": { + "properties": { + "code": { + "description": "The status code", + "example": 404, + "format": "int64", + "type": "integer" + }, + "debug": { + "description": "Debug information\n\nThis field is often not exposed to protect against leaking\nsensitive information.", + "example": "SQL field \"foo\" is not a bool.", + "type": "string" + }, + "details": { + "description": "Further error details" + }, + "id": { + "description": "The error ID\n\nUseful when trying to identify various errors in application logic.", + "type": "string" + }, + "message": { + "description": "Error message\n\nThe error's message.", + "example": "The resource could not be found", + "type": "string" + }, + "reason": { + "description": "A human-readable reason for the error", + "example": "User with ID 1234 does not exist.", + "type": "string" + }, + "request": { + "description": "The request ID\n\nThe request ID is often exposed internally in order to trace\nerrors across service architectures. This is often a UUID.", + "example": "d7ef54b1-ec15-46e6-bccb-524b82c035e6", + "type": "string" + }, + "status": { + "description": "The status description", + "example": "Not Found", + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "healthNotReadyStatus": { + "properties": { + "errors": { + "additionalProperties": { + "type": "string" + }, + "description": "Errors contains a list of errors that caused the not ready status.", + "type": "object" + } + }, + "type": "object" + }, + "healthStatus": { + "properties": { + "status": { + "description": "Status always contains \"ok\".", + "type": "string" + } + }, + "type": "object" + }, + "introspectedOAuth2Token": { + "description": "Introspection contains an access token's session data as specified by\n[IETF RFC 7662](https://tools.ietf.org/html/rfc7662)", + "properties": { + "active": { + "description": "Active is a boolean indicator of whether or not the presented token\nis currently active. The specifics of a token's \"active\" state\nwill vary depending on the implementation of the authorization\nserver and the information it keeps about its tokens, but a \"true\"\nvalue return for the \"active\" property will generally indicate\nthat a given token has been issued by this authorization server,\nhas not been revoked by the resource owner, and is within its\ngiven time window of validity (e.g., after its issuance time and\nbefore its expiration time).", + "type": "boolean" + }, + "aud": { + "description": "Audience contains a list of the token's intended audiences.", + "items": { + "type": "string" + }, + "type": "array" + }, + "client_id": { + "description": "ID is aclient identifier for the OAuth 2.0 client that\nrequested this token.", + "type": "string" + }, + "exp": { + "description": "Expires at is an integer timestamp, measured in the number of seconds\nsince January 1 1970 UTC, indicating when this token will expire.", + "format": "int64", + "type": "integer" + }, + "ext": { + "additionalProperties": {}, + "description": "Extra is arbitrary data set by the session.", + "type": "object" + }, + "iat": { + "description": "Issued at is an integer timestamp, measured in the number of seconds\nsince January 1 1970 UTC, indicating when this token was\noriginally issued.", + "format": "int64", + "type": "integer" + }, + "iss": { + "description": "IssuerURL is a string representing the issuer of this token", + "type": "string" + }, + "nbf": { + "description": "NotBefore is an integer timestamp, measured in the number of seconds\nsince January 1 1970 UTC, indicating when this token is not to be\nused before.", + "format": "int64", + "type": "integer" + }, + "obfuscated_subject": { + "description": "ObfuscatedSubject is set when the subject identifier algorithm was set to \"pairwise\" during authorization.\nIt is the `sub` value of the ID Token that was issued.", + "type": "string" + }, + "scope": { + "description": "Scope is a JSON string containing a space-separated list of\nscopes associated with this token.", + "type": "string" + }, + "sub": { + "description": "Subject of the token, as defined in JWT [RFC7519].\nUsually a machine-readable identifier of the resource owner who\nauthorized this token.", + "type": "string" + }, + "token_type": { + "description": "TokenType is the introspected token's type, typically `Bearer`.", + "type": "string" + }, + "token_use": { + "description": "TokenUse is the introspected token's use, for example `access_token` or `refresh_token`.", + "type": "string" + }, + "username": { + "description": "Username is a human-readable identifier for the resource owner who\nauthorized this token.", + "type": "string" + } + }, + "required": [ + "active" + ], + "type": "object" + }, + "jsonPatch": { + "description": "A JSONPatch document as defined by RFC 6902", + "properties": { + "from": { + "description": "This field is used together with operation \"move\" and uses JSON Pointer notation.\n\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).", + "example": "/name", + "type": "string" + }, + "op": { + "description": "The operation to be performed. One of \"add\", \"remove\", \"replace\", \"move\", \"copy\", or \"test\".", + "example": "replace", + "type": "string" + }, + "path": { + "description": "The path to the target path. Uses JSON pointer notation.\n\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).", + "example": "/name", + "type": "string" + }, + "value": { + "description": "The value to be used within the operations.\n\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).", + "example": "foobar" + } + }, + "required": [ + "op", + "path" + ], + "type": "object" + }, + "jsonPatchDocument": { + "description": "A JSONPatchDocument request", + "items": { + "$ref": "#/components/schemas/jsonPatch" + }, + "type": "array" + }, + "jsonWebKey": { + "properties": { + "alg": { + "description": "The \"alg\" (algorithm) parameter identifies the algorithm intended for\nuse with the key. The values used should either be registered in the\nIANA \"JSON Web Signature and Encryption Algorithms\" registry\nestablished by [JWA] or be a value that contains a Collision-\nResistant Name.", + "example": "RS256", + "type": "string" + }, + "crv": { + "example": "P-256", + "type": "string" + }, + "d": { + "example": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "type": "string" + }, + "dp": { + "example": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "type": "string" + }, + "dq": { + "example": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "type": "string" + }, + "e": { + "example": "AQAB", + "type": "string" + }, + "k": { + "example": "GawgguFyGrWKav7AX4VKUg", + "type": "string" + }, + "kid": { + "description": "The \"kid\" (key ID) parameter is used to match a specific key. This\nis used, for instance, to choose among a set of keys within a JWK Set\nduring key rollover. The structure of the \"kid\" value is\nunspecified. When \"kid\" values are used within a JWK Set, different\nkeys within the JWK Set SHOULD use distinct \"kid\" values. (One\nexample in which different keys might use the same \"kid\" value is if\nthey have different \"kty\" (key type) values but are considered to be\nequivalent alternatives by the application using them.) The \"kid\"\nvalue is a case-sensitive string.", + "example": "1603dfe0af8f4596", + "type": "string" + }, + "kty": { + "description": "The \"kty\" (key type) parameter identifies the cryptographic algorithm\nfamily used with the key, such as \"RSA\" or \"EC\". \"kty\" values should\neither be registered in the IANA \"JSON Web Key Types\" registry\nestablished by [JWA] or be a value that contains a Collision-\nResistant Name. The \"kty\" value is a case-sensitive string.", + "example": "RSA", + "type": "string" + }, + "n": { + "example": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "type": "string" + }, + "p": { + "example": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "type": "string" + }, + "q": { + "example": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "type": "string" + }, + "qi": { + "example": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "type": "string" + }, + "use": { + "description": "Use (\"public key use\") identifies the intended use of\nthe public key. The \"use\" parameter is employed to indicate whether\na public key is used for encrypting data or verifying the signature\non data. Values are commonly \"sig\" (signature) or \"enc\" (encryption).", + "example": "sig", + "type": "string" + }, + "x": { + "example": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "type": "string" + }, + "x5c": { + "description": "The \"x5c\" (X.509 certificate chain) parameter contains a chain of one\nor more PKIX certificates [RFC5280]. The certificate chain is\nrepresented as a JSON array of certificate value strings. Each\nstring in the array is a base64-encoded (Section 4 of [RFC4648] --\nnot base64url-encoded) DER [ITU.X690.1994] PKIX certificate value.\nThe PKIX certificate containing the key value MUST be the first\ncertificate.", + "items": { + "type": "string" + }, + "type": "array" + }, + "y": { + "example": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0", + "type": "string" + } + }, + "required": [ + "use", + "kty", + "kid", + "alg" + ], + "type": "object" + }, + "jsonWebKeySet": { + "description": "JSON Web Key Set", + "properties": { + "keys": { + "description": "List of JSON Web Keys\n\nThe value of the \"keys\" parameter is an array of JSON Web Key (JWK)\nvalues. By default, the order of the JWK values within the array does\nnot imply an order of preference among them, although applications\nof JWK Sets can choose to assign a meaning to the order for their\npurposes, if desired.", + "items": { + "$ref": "#/components/schemas/jsonWebKey" + }, + "type": "array" + } + }, + "type": "object" + }, + "nullDuration": { + "nullable": true, + "pattern": "^[0-9]+(ns|us|ms|s|m|h)$", + "type": "string" + }, + "nullInt64": { + "nullable": true, + "type": "integer" + }, + "nullTime": { + "format": "date-time", + "title": "NullTime implements sql.NullTime functionality.", + "type": "string" + }, + "oAuth2Client": { + "description": "OAuth 2.0 Clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "properties": { + "access_token_strategy": { + "description": "OAuth 2.0 Access Token Strategy\n\nAccessTokenStrategy is the strategy used to generate access tokens.\nValid options are `jwt` and `opaque`. `jwt` is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens\nSetting the stragegy here overrides the global setting in `strategies.access_token`.", + "type": "string" + }, + "allowed_cors_origins": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "authorization_code_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "authorization_code_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "authorization_code_grant_refresh_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "backchannel_logout_session_required": { + "description": "OpenID Connect Back-Channel Logout Session Required\n\nBoolean value specifying whether the RP requires that a sid (session ID) Claim be included in the Logout\nToken to identify the RP session with the OP when the backchannel_logout_uri is used.\nIf omitted, the default value is false.", + "type": "boolean" + }, + "backchannel_logout_uri": { + "description": "OpenID Connect Back-Channel Logout URI\n\nRP URL that will cause the RP to log itself out when sent a Logout Token by the OP.", + "type": "string" + }, + "client_credentials_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "client_id": { + "description": "OAuth 2.0 Client ID\n\nThe ID is autogenerated and immutable.", + "type": "string" + }, + "client_name": { + "description": "OAuth 2.0 Client Name\n\nThe human-readable name of the client to be presented to the\nend-user during authorization.", + "type": "string" + }, + "client_secret": { + "description": "OAuth 2.0 Client Secret\n\nThe secret will be included in the create request as cleartext, and then\nnever again. The secret is kept in hashed format and is not recoverable once lost.", + "type": "string" + }, + "client_secret_expires_at": { + "description": "OAuth 2.0 Client Secret Expires At\n\nThe field is currently not supported and its value is always 0.", + "format": "int64", + "type": "integer" + }, + "client_uri": { + "description": "OAuth 2.0 Client URI\n\nClientURI is a URL string of a web page providing information about the client.\nIf present, the server SHOULD display this URL to the end-user in\na clickable fashion.", + "type": "string" + }, + "contacts": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "created_at": { + "description": "OAuth 2.0 Client Creation Date\n\nCreatedAt returns the timestamp of the client's creation.", + "format": "date-time", + "type": "string" + }, + "frontchannel_logout_session_required": { + "description": "OpenID Connect Front-Channel Logout Session Required\n\nBoolean value specifying whether the RP requires that iss (issuer) and sid (session ID) query parameters be\nincluded to identify the RP session with the OP when the frontchannel_logout_uri is used.\nIf omitted, the default value is false.", + "type": "boolean" + }, + "frontchannel_logout_uri": { + "description": "OpenID Connect Front-Channel Logout URI\n\nRP URL that will cause the RP to log itself out when rendered in an iframe by the OP. An iss (issuer) query\nparameter and a sid (session ID) query parameter MAY be included by the OP to enable the RP to validate the\nrequest and to determine which of the potentially multiple sessions is to be logged out; if either is\nincluded, both MUST be.", + "type": "string" + }, + "grant_types": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "implicit_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "implicit_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "jwks": { + "description": "OAuth 2.0 Client JSON Web Key Set\n\nClient's JSON Web Key Set [JWK] document, passed by value. The semantics of the jwks parameter are the same as\nthe jwks_uri parameter, other than that the JWK Set is passed by value, rather than by reference. This parameter\nis intended only to be used by Clients that, for some reason, are unable to use the jwks_uri parameter, for\ninstance, by native applications that might not have a location to host the contents of the JWK Set. If a Client\ncan use jwks_uri, it MUST NOT use jwks. One significant downside of jwks is that it does not enable key rotation\n(which jwks_uri does, as described in Section 10 of OpenID Connect Core 1.0 [OpenID.Core]). The jwks_uri and jwks\nparameters MUST NOT be used together." + }, + "jwks_uri": { + "description": "OAuth 2.0 Client JSON Web Key Set URL\n\nURL for the Client's JSON Web Key Set [JWK] document. If the Client signs requests to the Server, it contains\nthe signing key(s) the Server uses to validate signatures from the Client. The JWK Set MAY also contain the\nClient's encryption keys(s), which are used by the Server to encrypt responses to the Client. When both signing\nand encryption keys are made available, a use (Key Use) parameter value is REQUIRED for all keys in the referenced\nJWK Set to indicate each key's intended usage. Although some algorithms allow the same key to be used for both\nsignatures and encryption, doing so is NOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used\nto provide X.509 representations of keys provided. When used, the bare key values MUST still be present and MUST\nmatch those in the certificate.", + "type": "string" + }, + "jwt_bearer_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "logo_uri": { + "description": "OAuth 2.0 Client Logo URI\n\nA URL string referencing the client's logo.", + "type": "string" + }, + "metadata": { + "$ref": "#/components/schemas/JSONRawMessage" + }, + "owner": { + "description": "OAuth 2.0 Client Owner\n\nOwner is a string identifying the owner of the OAuth 2.0 Client.", + "type": "string" + }, + "policy_uri": { + "description": "OAuth 2.0 Client Policy URI\n\nPolicyURI is a URL string that points to a human-readable privacy policy document\nthat describes how the deployment organization collects, uses,\nretains, and discloses personal data.", + "type": "string" + }, + "post_logout_redirect_uris": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "redirect_uris": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "refresh_token_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_refresh_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "registration_access_token": { + "description": "OpenID Connect Dynamic Client Registration Access Token\n\nRegistrationAccessToken can be used to update, get, or delete the OAuth2 Client. It is sent when creating a client\nusing Dynamic Client Registration.", + "type": "string" + }, + "registration_client_uri": { + "description": "OpenID Connect Dynamic Client Registration URL\n\nRegistrationClientURI is the URL used to update, get, or delete the OAuth2 Client.", + "type": "string" + }, + "request_object_signing_alg": { + "description": "OpenID Connect Request Object Signing Algorithm\n\nJWS [JWS] alg algorithm [JWA] that MUST be used for signing Request Objects sent to the OP. All Request Objects\nfrom this Client MUST be rejected, if not signed with this algorithm.", + "type": "string" + }, + "request_uris": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "response_types": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "scope": { + "description": "OAuth 2.0 Client Scope\n\nScope is a string containing a space-separated list of scope values (as\ndescribed in Section 3.3 of OAuth 2.0 [RFC6749]) that the client\ncan use when requesting access tokens.", + "example": "scope1 scope-2 scope.3 scope:4", + "type": "string" + }, + "sector_identifier_uri": { + "description": "OpenID Connect Sector Identifier URI\n\nURL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a\nfile with a single JSON array of redirect_uri values.", + "type": "string" + }, + "skip_consent": { + "description": "SkipConsent skips the consent screen for this client. This field can only\nbe set from the admin API.", + "type": "boolean" + }, + "subject_type": { + "description": "OpenID Connect Subject Type\n\nThe `subject_types_supported` Discovery parameter contains a\nlist of the supported subject_type values for this server. Valid types include `pairwise` and `public`.", + "type": "string" + }, + "token_endpoint_auth_method": { + "default": "client_secret_basic", + "description": "OAuth 2.0 Token Endpoint Authentication Method\n\nRequested Client Authentication method for the Token Endpoint. The options are:\n\n`client_secret_basic`: (default) Send `client_id` and `client_secret` as `application/x-www-form-urlencoded` encoded in the HTTP Authorization header.\n`client_secret_post`: Send `client_id` and `client_secret` as `application/x-www-form-urlencoded` in the HTTP body.\n`private_key_jwt`: Use JSON Web Tokens to authenticate the client.\n`none`: Used for public clients (native apps, mobile apps) which can not have secrets.", + "type": "string" + }, + "token_endpoint_auth_signing_alg": { + "description": "OAuth 2.0 Token Endpoint Signing Algorithm\n\nRequested Client Authentication signing algorithm for the Token Endpoint.", + "type": "string" + }, + "tos_uri": { + "description": "OAuth 2.0 Client Terms of Service URI\n\nA URL string pointing to a human-readable terms of service\ndocument for the client that describes a contractual relationship\nbetween the end-user and the client that the end-user accepts when\nauthorizing the client.", + "type": "string" + }, + "updated_at": { + "description": "OAuth 2.0 Client Last Update Date\n\nUpdatedAt returns the timestamp of the last update.", + "format": "date-time", + "type": "string" + }, + "userinfo_signed_response_alg": { + "description": "OpenID Connect Request Userinfo Signed Response Algorithm\n\nJWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT\n[JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims\nas a UTF-8 encoded JSON object using the application/json content-type.", + "type": "string" + } + }, + "title": "OAuth 2.0 Client", + "type": "object" + }, + "oAuth2ClientTokenLifespans": { + "description": "Lifespans of different token types issued for this OAuth 2.0 Client.", + "properties": { + "authorization_code_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "authorization_code_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "authorization_code_grant_refresh_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "client_credentials_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "implicit_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "implicit_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "jwt_bearer_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_access_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_id_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + }, + "refresh_token_grant_refresh_token_lifespan": { + "$ref": "#/components/schemas/NullDuration" + } + }, + "title": "OAuth 2.0 Client Token Lifespans", + "type": "object" + }, + "oAuth2ConsentRequest": { + "properties": { + "acr": { + "description": "ACR represents the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it\nto express that, for example, a user authenticated using two factor authentication.", + "type": "string" + }, + "amr": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "challenge": { + "description": "ID is the identifier (\"authorization challenge\") of the consent authorization request. It is used to\nidentify the session.", + "type": "string" + }, + "client": { + "$ref": "#/components/schemas/oAuth2Client" + }, + "context": { + "$ref": "#/components/schemas/JSONRawMessage" + }, + "login_challenge": { + "description": "LoginChallenge is the login challenge this consent challenge belongs to. It can be used to associate\na login and consent request in the login & consent app.", + "type": "string" + }, + "login_session_id": { + "description": "LoginSessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag)\nthis ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false)\nthis will be a new random value. This value is used as the \"sid\" parameter in the ID Token and in OIDC Front-/Back-\nchannel logout. It's value can generally be used to associate consecutive login requests by a certain user.", + "type": "string" + }, + "oidc_context": { + "$ref": "#/components/schemas/oAuth2ConsentRequestOpenIDConnectContext" + }, + "request_url": { + "description": "RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which\ninitiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but\nmight come in handy if you want to deal with additional request parameters.", + "type": "string" + }, + "requested_access_token_audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "requested_scope": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "skip": { + "description": "Skip, if true, implies that the client has requested the same scopes from the same user previously.\nIf true, you must not ask the user to grant the requested scopes. You must however either allow or deny the\nconsent request using the usual API call.", + "type": "boolean" + }, + "subject": { + "description": "Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope\nrequested by the OAuth 2.0 client.", + "type": "string" + } + }, + "required": [ + "challenge" + ], + "title": "Contains information on an ongoing consent request.", + "type": "object" + }, + "oAuth2ConsentRequestOpenIDConnectContext": { + "properties": { + "acr_values": { + "description": "ACRValues is the Authentication AuthorizationContext Class Reference requested in the OAuth 2.0 Authorization request.\nIt is a parameter defined by OpenID Connect and expresses which level of authentication (e.g. 2FA) is required.\n\nOpenID Connect defines it as follows:\n> Requested Authentication AuthorizationContext Class Reference values. Space-separated string that specifies the acr values\nthat the Authorization Server is being requested to use for processing this Authentication Request, with the\nvalues appearing in order of preference. The Authentication AuthorizationContext Class satisfied by the authentication\nperformed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a\nVoluntary Claim by this parameter.", + "items": { + "type": "string" + }, + "type": "array" + }, + "display": { + "description": "Display is a string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User.\nThe defined values are:\npage: The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. If the display parameter is not specified, this is the default display mode.\npopup: The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over.\ntouch: The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface.\nwap: The Authorization Server SHOULD display the authentication and consent UI consistent with a \"feature phone\" type display.\n\nThe Authorization Server MAY also attempt to detect the capabilities of the User Agent and present an appropriate display.", + "type": "string" + }, + "id_token_hint_claims": { + "additionalProperties": {}, + "description": "IDTokenHintClaims are the claims of the ID Token previously issued by the Authorization Server being passed as a hint about the\nEnd-User's current or past authenticated session with the Client.", + "type": "object" + }, + "login_hint": { + "description": "LoginHint hints about the login identifier the End-User might use to log in (if necessary).\nThis hint can be used by an RP if it first asks the End-User for their e-mail address (or other identifier)\nand then wants to pass that value as a hint to the discovered authorization service. This value MAY also be a\nphone number in the format specified for the phone_number Claim. The use of this parameter is optional.", + "type": "string" + }, + "ui_locales": { + "description": "UILocales is the End-User'id preferred languages and scripts for the user interface, represented as a\nspace-separated list of BCP47 [RFC5646] language tag values, ordered by preference. For instance, the value\n\"fr-CA fr en\" represents a preference for French as spoken in Canada, then French (without a region designation),\nfollowed by English (without a region designation). An error SHOULD NOT result if some or all of the requested\nlocales are not supported by the OpenID Provider.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "title": "Contains optional information about the OpenID Connect request.", + "type": "object" + }, + "oAuth2ConsentSession": { + "description": "A completed OAuth 2.0 Consent Session.", + "properties": { + "consent_request": { + "$ref": "#/components/schemas/oAuth2ConsentRequest" + }, + "expires_at": { + "properties": { + "access_token": { + "format": "date-time", + "type": "string" + }, + "authorize_code": { + "format": "date-time", + "type": "string" + }, + "id_token": { + "format": "date-time", + "type": "string" + }, + "par_context": { + "format": "date-time", + "type": "string" + }, + "refresh_token": { + "format": "date-time", + "type": "string" + } + }, + "type": "object" + }, + "grant_access_token_audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "grant_scope": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "handled_at": { + "$ref": "#/components/schemas/nullTime" + }, + "remember": { + "description": "Remember Consent\n\nRemember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same\nclient asks the same user for the same, or a subset of, scope.", + "type": "boolean" + }, + "remember_for": { + "description": "Remember Consent For\n\nRememberFor sets how long the consent authorization should be remembered for in seconds. If set to `0`, the\nauthorization will be remembered indefinitely.", + "format": "int64", + "type": "integer" + }, + "session": { + "$ref": "#/components/schemas/acceptOAuth2ConsentRequestSession" + } + }, + "title": "OAuth 2.0 Consent Session", + "type": "object" + }, + "oAuth2ConsentSessions": { + "description": "List of OAuth 2.0 Consent Sessions", + "items": { + "$ref": "#/components/schemas/oAuth2ConsentSession" + }, + "type": "array" + }, + "oAuth2LoginRequest": { + "properties": { + "challenge": { + "description": "ID is the identifier (\"login challenge\") of the login request. It is used to\nidentify the session.", + "type": "string" + }, + "client": { + "$ref": "#/components/schemas/oAuth2Client" + }, + "oidc_context": { + "$ref": "#/components/schemas/oAuth2ConsentRequestOpenIDConnectContext" + }, + "request_url": { + "description": "RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which\ninitiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but\nmight come in handy if you want to deal with additional request parameters.", + "type": "string" + }, + "requested_access_token_audience": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "requested_scope": { + "$ref": "#/components/schemas/StringSliceJSONFormat" + }, + "session_id": { + "description": "SessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag)\nthis ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false)\nthis will be a new random value. This value is used as the \"sid\" parameter in the ID Token and in OIDC Front-/Back-\nchannel logout. It's value can generally be used to associate consecutive login requests by a certain user.", + "type": "string" + }, + "skip": { + "description": "Skip, if true, implies that the client has requested the same scopes from the same user previously.\nIf true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL.\n\nThis feature allows you to update / set session information.", + "type": "boolean" + }, + "subject": { + "description": "Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope\nrequested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type\nwhen accepting the login request, or the request will fail.", + "type": "string" + } + }, + "required": [ + "challenge", + "requested_scope", + "requested_access_token_audience", + "skip", + "subject", + "client", + "request_url" + ], + "title": "Contains information on an ongoing login request.", + "type": "object" + }, + "oAuth2LogoutRequest": { + "properties": { + "challenge": { + "description": "Challenge is the identifier (\"logout challenge\") of the logout authentication request. It is used to\nidentify the session.", + "type": "string" + }, + "client": { + "$ref": "#/components/schemas/oAuth2Client" + }, + "request_url": { + "description": "RequestURL is the original Logout URL requested.", + "type": "string" + }, + "rp_initiated": { + "description": "RPInitiated is set to true if the request was initiated by a Relying Party (RP), also known as an OAuth 2.0 Client.", + "type": "boolean" + }, + "sid": { + "description": "SessionID is the login session ID that was requested to log out.", + "type": "string" + }, + "subject": { + "description": "Subject is the user for whom the logout was request.", + "type": "string" + } + }, + "title": "Contains information about an ongoing logout request.", + "type": "object" + }, + "oAuth2RedirectTo": { + "description": "Contains a redirect URL used to complete a login, consent, or logout request.", + "properties": { + "redirect_to": { + "description": "RedirectURL is the URL which you should redirect the user's browser to once the authentication process is completed.", + "type": "string" + } + }, + "required": [ + "redirect_to" + ], + "title": "OAuth 2.0 Redirect Browser To", + "type": "object" + }, + "oAuth2TokenExchange": { + "description": "OAuth2 Token Exchange Result", + "properties": { + "access_token": { + "description": "The access token issued by the authorization server.", + "type": "string" + }, + "expires_in": { + "description": "The lifetime in seconds of the access token. For\nexample, the value \"3600\" denotes that the access token will\nexpire in one hour from the time the response was generated.", + "format": "int64", + "type": "integer" + }, + "id_token": { + "description": "To retrieve a refresh token request the id_token scope.", + "format": "int64", + "type": "integer" + }, + "refresh_token": { + "description": "The refresh token, which can be used to obtain new\naccess tokens. To retrieve it add the scope \"offline\" to your access token request.", + "type": "string" + }, + "scope": { + "description": "The scope of the access token", + "type": "string" + }, + "token_type": { + "description": "The type of the token issued", + "type": "string" + } + }, + "type": "object" + }, + "oidcConfiguration": { + "description": "Includes links to several endpoints (for example `/oauth2/token`) and exposes information on supported signature algorithms\namong others.", + "properties": { + "authorization_endpoint": { + "description": "OAuth 2.0 Authorization Endpoint URL", + "example": "https://playground.ory.sh/ory-hydra/public/oauth2/auth", + "type": "string" + }, + "backchannel_logout_session_supported": { + "description": "OpenID Connect Back-Channel Logout Session Required\n\nBoolean value specifying whether the OP can pass a sid (session ID) Claim in the Logout Token to identify the RP\nsession with the OP. If supported, the sid Claim is also included in ID Tokens issued by the OP", + "type": "boolean" + }, + "backchannel_logout_supported": { + "description": "OpenID Connect Back-Channel Logout Supported\n\nBoolean value specifying whether the OP supports back-channel logout, with true indicating support.", + "type": "boolean" + }, + "claims_parameter_supported": { + "description": "OpenID Connect Claims Parameter Parameter Supported\n\nBoolean value specifying whether the OP supports use of the claims parameter, with true indicating support.", + "type": "boolean" + }, + "claims_supported": { + "description": "OpenID Connect Supported Claims\n\nJSON array containing a list of the Claim Names of the Claims that the OpenID Provider MAY be able to supply\nvalues for. Note that for privacy or other reasons, this might not be an exhaustive list.", + "items": { + "type": "string" + }, + "type": "array" + }, + "code_challenge_methods_supported": { + "description": "OAuth 2.0 PKCE Supported Code Challenge Methods\n\nJSON array containing a list of Proof Key for Code Exchange (PKCE) [RFC7636] code challenge methods supported\nby this authorization server.", + "items": { + "type": "string" + }, + "type": "array" + }, + "end_session_endpoint": { + "description": "OpenID Connect End-Session Endpoint\n\nURL at the OP to which an RP can perform a redirect to request that the End-User be logged out at the OP.", + "type": "string" + }, + "frontchannel_logout_session_supported": { + "description": "OpenID Connect Front-Channel Logout Session Required\n\nBoolean value specifying whether the OP can pass iss (issuer) and sid (session ID) query parameters to identify\nthe RP session with the OP when the frontchannel_logout_uri is used. If supported, the sid Claim is also\nincluded in ID Tokens issued by the OP.", + "type": "boolean" + }, + "frontchannel_logout_supported": { + "description": "OpenID Connect Front-Channel Logout Supported\n\nBoolean value specifying whether the OP supports HTTP-based logout, with true indicating support.", + "type": "boolean" + }, + "grant_types_supported": { + "description": "OAuth 2.0 Supported Grant Types\n\nJSON array containing a list of the OAuth 2.0 Grant Type values that this OP supports.", + "items": { + "type": "string" + }, + "type": "array" + }, + "id_token_signed_response_alg": { + "description": "OpenID Connect Default ID Token Signing Algorithms\n\nAlgorithm used to sign OpenID Connect ID Tokens.", + "items": { + "type": "string" + }, + "type": "array" + }, + "id_token_signing_alg_values_supported": { + "description": "OpenID Connect Supported ID Token Signing Algorithms\n\nJSON array containing a list of the JWS signing algorithms (alg values) supported by the OP for the ID Token\nto encode the Claims in a JWT.", + "items": { + "type": "string" + }, + "type": "array" + }, + "issuer": { + "description": "OpenID Connect Issuer URL\n\nAn URL using the https scheme with no query or fragment component that the OP asserts as its IssuerURL Identifier.\nIf IssuerURL discovery is supported , this value MUST be identical to the issuer value returned\nby WebFinger. This also MUST be identical to the iss Claim value in ID Tokens issued from this IssuerURL.", + "example": "https://playground.ory.sh/ory-hydra/public/", + "type": "string" + }, + "jwks_uri": { + "description": "OpenID Connect Well-Known JSON Web Keys URL\n\nURL of the OP's JSON Web Key Set [JWK] document. This contains the signing key(s) the RP uses to validate\nsignatures from the OP. The JWK Set MAY also contain the Server's encryption key(s), which are used by RPs\nto encrypt requests to the Server. When both signing and encryption keys are made available, a use (Key Use)\nparameter value is REQUIRED for all keys in the referenced JWK Set to indicate each key's intended usage.\nAlthough some algorithms allow the same key to be used for both signatures and encryption, doing so is\nNOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of\nkeys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.", + "example": "https://{slug}.projects.oryapis.com/.well-known/jwks.json", + "type": "string" + }, + "registration_endpoint": { + "description": "OpenID Connect Dynamic Client Registration Endpoint URL", + "example": "https://playground.ory.sh/ory-hydra/admin/client", + "type": "string" + }, + "request_object_signing_alg_values_supported": { + "description": "OpenID Connect Supported Request Object Signing Algorithms\n\nJSON array containing a list of the JWS signing algorithms (alg values) supported by the OP for Request Objects,\nwhich are described in Section 6.1 of OpenID Connect Core 1.0 [OpenID.Core]. These algorithms are used both when\nthe Request Object is passed by value (using the request parameter) and when it is passed by reference\n(using the request_uri parameter).", + "items": { + "type": "string" + }, + "type": "array" + }, + "request_parameter_supported": { + "description": "OpenID Connect Request Parameter Supported\n\nBoolean value specifying whether the OP supports use of the request parameter, with true indicating support.", + "type": "boolean" + }, + "request_uri_parameter_supported": { + "description": "OpenID Connect Request URI Parameter Supported\n\nBoolean value specifying whether the OP supports use of the request_uri parameter, with true indicating support.", + "type": "boolean" + }, + "require_request_uri_registration": { + "description": "OpenID Connect Requires Request URI Registration\n\nBoolean value specifying whether the OP requires any request_uri values used to be pre-registered\nusing the request_uris registration parameter.", + "type": "boolean" + }, + "response_modes_supported": { + "description": "OAuth 2.0 Supported Response Modes\n\nJSON array containing a list of the OAuth 2.0 response_mode values that this OP supports.", + "items": { + "type": "string" + }, + "type": "array" + }, + "response_types_supported": { + "description": "OAuth 2.0 Supported Response Types\n\nJSON array containing a list of the OAuth 2.0 response_type values that this OP supports. Dynamic OpenID\nProviders MUST support the code, id_token, and the token id_token Response Type values.", + "items": { + "type": "string" + }, + "type": "array" + }, + "revocation_endpoint": { + "description": "OAuth 2.0 Token Revocation URL\n\nURL of the authorization server's OAuth 2.0 revocation endpoint.", + "type": "string" + }, + "scopes_supported": { + "description": "OAuth 2.0 Supported Scope Values\n\nJSON array containing a list of the OAuth 2.0 [RFC6749] scope values that this server supports. The server MUST\nsupport the openid scope value. Servers MAY choose not to advertise some supported scope values even when this parameter is used", + "items": { + "type": "string" + }, + "type": "array" + }, + "subject_types_supported": { + "description": "OpenID Connect Supported Subject Types\n\nJSON array containing a list of the Subject Identifier types that this OP supports. Valid types include\npairwise and public.", + "items": { + "type": "string" + }, + "type": "array" + }, + "token_endpoint": { + "description": "OAuth 2.0 Token Endpoint URL", + "example": "https://playground.ory.sh/ory-hydra/public/oauth2/token", + "type": "string" + }, + "token_endpoint_auth_methods_supported": { + "description": "OAuth 2.0 Supported Client Authentication Methods\n\nJSON array containing a list of Client Authentication methods supported by this Token Endpoint. The options are\nclient_secret_post, client_secret_basic, client_secret_jwt, and private_key_jwt, as described in Section 9 of OpenID Connect Core 1.0", + "items": { + "type": "string" + }, + "type": "array" + }, + "userinfo_endpoint": { + "description": "OpenID Connect Userinfo URL\n\nURL of the OP's UserInfo Endpoint.", + "type": "string" + }, + "userinfo_signed_response_alg": { + "description": "OpenID Connect User Userinfo Signing Algorithm\n\nAlgorithm used to sign OpenID Connect Userinfo Responses.", + "items": { + "type": "string" + }, + "type": "array" + }, + "userinfo_signing_alg_values_supported": { + "description": "OpenID Connect Supported Userinfo Signing Algorithm\n\nJSON array containing a list of the JWS [JWS] signing algorithms (alg values) [JWA] supported by the UserInfo Endpoint to encode the Claims in a JWT [JWT].", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "issuer", + "authorization_endpoint", + "token_endpoint", + "jwks_uri", + "subject_types_supported", + "response_types_supported", + "id_token_signing_alg_values_supported", + "id_token_signed_response_alg", + "userinfo_signed_response_alg" + ], + "title": "OpenID Connect Discovery Metadata", + "type": "object" + }, + "oidcUserInfo": { + "description": "OpenID Connect Userinfo", + "properties": { + "birthdate": { + "description": "End-User's birthday, represented as an ISO 8601:2004 [ISO8601‑2004] YYYY-MM-DD format. The year MAY be 0000, indicating that it is omitted. To represent only the year, YYYY format is allowed. Note that depending on the underlying platform's date related function, providing just year can result in varying month and day, so the implementers need to take this factor into account to correctly process the dates.", + "type": "string" + }, + "email": { + "description": "End-User's preferred e-mail address. Its value MUST conform to the RFC 5322 [RFC5322] addr-spec syntax. The RP MUST NOT rely upon this value being unique, as discussed in Section 5.7.", + "type": "string" + }, + "email_verified": { + "description": "True if the End-User's e-mail address has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this e-mail address was controlled by the End-User at the time the verification was performed. The means by which an e-mail address is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating.", + "type": "boolean" + }, + "family_name": { + "description": "Surname(s) or last name(s) of the End-User. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters.", + "type": "string" + }, + "gender": { + "description": "End-User's gender. Values defined by this specification are female and male. Other values MAY be used when neither of the defined values are applicable.", + "type": "string" + }, + "given_name": { + "description": "Given name(s) or first name(s) of the End-User. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters.", + "type": "string" + }, + "locale": { + "description": "End-User's locale, represented as a BCP47 [RFC5646] language tag. This is typically an ISO 639-1 Alpha-2 [ISO639‑1] language code in lowercase and an ISO 3166-1 Alpha-2 [ISO3166‑1] country code in uppercase, separated by a dash. For example, en-US or fr-CA. As a compatibility note, some implementations have used an underscore as the separator rather than a dash, for example, en_US; Relying Parties MAY choose to accept this locale syntax as well.", + "type": "string" + }, + "middle_name": { + "description": "Middle name(s) of the End-User. Note that in some cultures, people can have multiple middle names; all can be present, with the names being separated by space characters. Also note that in some cultures, middle names are not used.", + "type": "string" + }, + "name": { + "description": "End-User's full name in displayable form including all name parts, possibly including titles and suffixes, ordered according to the End-User's locale and preferences.", + "type": "string" + }, + "nickname": { + "description": "Casual name of the End-User that may or may not be the same as the given_name. For instance, a nickname value of Mike might be returned alongside a given_name value of Michael.", + "type": "string" + }, + "phone_number": { + "description": "End-User's preferred telephone number. E.164 [E.164] is RECOMMENDED as the format of this Claim, for example, +1 (425) 555-1212 or +56 (2) 687 2400. If the phone number contains an extension, it is RECOMMENDED that the extension be represented using the RFC 3966 [RFC3966] extension syntax, for example, +1 (604) 555-1234;ext=5678.", + "type": "string" + }, + "phone_number_verified": { + "description": "True if the End-User's phone number has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this phone number was controlled by the End-User at the time the verification was performed. The means by which a phone number is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating. When true, the phone_number Claim MUST be in E.164 format and any extensions MUST be represented in RFC 3966 format.", + "type": "boolean" + }, + "picture": { + "description": "URL of the End-User's profile picture. This URL MUST refer to an image file (for example, a PNG, JPEG, or GIF image file), rather than to a Web page containing an image. Note that this URL SHOULD specifically reference a profile photo of the End-User suitable for displaying when describing the End-User, rather than an arbitrary photo taken by the End-User.", + "type": "string" + }, + "preferred_username": { + "description": "Non-unique shorthand name by which the End-User wishes to be referred to at the RP, such as janedoe or j.doe. This value MAY be any valid JSON string including special characters such as @, /, or whitespace.", + "type": "string" + }, + "profile": { + "description": "URL of the End-User's profile page. The contents of this Web page SHOULD be about the End-User.", + "type": "string" + }, + "sub": { + "description": "Subject - Identifier for the End-User at the IssuerURL.", + "type": "string" + }, + "updated_at": { + "description": "Time the End-User's information was last updated. Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time.", + "format": "int64", + "type": "integer" + }, + "website": { + "description": "URL of the End-User's Web page or blog. This Web page SHOULD contain information published by the End-User or an organization that the End-User is affiliated with.", + "type": "string" + }, + "zoneinfo": { + "description": "String from zoneinfo [zoneinfo] time zone database representing the End-User's time zone. For example, Europe/Paris or America/Los_Angeles.", + "type": "string" + } + }, + "type": "object" + }, + "pagination": { + "properties": { + "page_size": { + "default": 250, + "description": "Items per page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "format": "int64", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "page_token": { + "default": "1", + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "minimum": 1, + "type": "string" + } + }, + "type": "object" + }, + "paginationHeaders": { + "properties": { + "link": { + "description": "The link header contains pagination links.\n\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\nin: header", + "type": "string" + }, + "x-total-count": { + "description": "The total number of clients.\n\nin: header", + "type": "string" + } + }, + "type": "object" + }, + "rejectOAuth2Request": { + "properties": { + "error": { + "description": "The error should follow the OAuth2 error format (e.g. `invalid_request`, `login_required`).\n\nDefaults to `request_denied`.", + "type": "string" + }, + "error_debug": { + "description": "Debug contains information to help resolve the problem as a developer. Usually not exposed\nto the public but only in the server logs.", + "type": "string" + }, + "error_description": { + "description": "Description of the error in a human readable format.", + "type": "string" + }, + "error_hint": { + "description": "Hint to help resolve the error.", + "type": "string" + }, + "status_code": { + "description": "Represents the HTTP status code of the error (e.g. 401 or 403)\n\nDefaults to 400", + "format": "int64", + "type": "integer" + } + }, + "title": "The request payload used to accept a login or consent request.", + "type": "object" + }, + "tokenPagination": { + "properties": { + "page_size": { + "default": 250, + "description": "Items per page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "format": "int64", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "page_token": { + "default": "1", + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "minimum": 1, + "type": "string" + } + }, + "type": "object" + }, + "tokenPaginationHeaders": { + "properties": { + "link": { + "description": "The link header contains pagination links.\n\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\nin: header", + "type": "string" + }, + "x-total-count": { + "description": "The total number of clients.\n\nin: header", + "type": "string" + } + }, + "type": "object" + }, + "tokenPaginationRequestParameters": { + "description": "The `Link` HTTP header contains multiple links (`first`, `next`, `last`, `previous`) formatted as:\n`; rel=\"{page}\"`\n\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "properties": { + "page_size": { + "default": 250, + "description": "Items per Page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "format": "int64", + "maximum": 500, + "minimum": 1, + "type": "integer" + }, + "page_token": { + "default": "1", + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "minimum": 1, + "type": "string" + } + }, + "title": "Pagination Request Parameters", + "type": "object" + }, + "tokenPaginationResponseHeaders": { + "description": "The `Link` HTTP header contains multiple links (`first`, `next`, `last`, `previous`) formatted as:\n`; rel=\"{page}\"`\n\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "properties": { + "link": { + "description": "The Link HTTP Header\n\nThe `Link` header contains a comma-delimited list of links to the following pages:\n\nfirst: The first page of results.\nnext: The next page of results.\nprev: The previous page of results.\nlast: The last page of results.\n\nPages are omitted if they do not exist. For example, if there is no next page, the `next` link is omitted. Examples:\n\n; rel=\"first\",; rel=\"next\",; rel=\"prev\",; rel=\"last\"", + "type": "string" + }, + "x-total-count": { + "description": "The X-Total-Count HTTP Header\n\nThe `X-Total-Count` header contains the total number of items in the collection.", + "format": "int64", + "type": "integer" + } + }, + "title": "Pagination Response Header", + "type": "object" + }, + "trustOAuth2JwtGrantIssuer": { + "description": "Trust OAuth2 JWT Bearer Grant Type Issuer Request Body", + "properties": { + "allow_any_subject": { + "description": "The \"allow_any_subject\" indicates that the issuer is allowed to have any principal as the subject of the JWT.", + "type": "boolean" + }, + "expires_at": { + "description": "The \"expires_at\" indicates, when grant will expire, so we will reject assertion from \"issuer\" targeting \"subject\".", + "format": "date-time", + "type": "string" + }, + "issuer": { + "description": "The \"issuer\" identifies the principal that issued the JWT assertion (same as \"iss\" claim in JWT).", + "example": "https://jwt-idp.example.com", + "type": "string" + }, + "jwk": { + "$ref": "#/components/schemas/jsonWebKey" + }, + "scope": { + "description": "The \"scope\" contains list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749])", + "example": [ + "openid", + "offline" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "subject": { + "description": "The \"subject\" identifies the principal that is the subject of the JWT.", + "example": "mike@example.com", + "type": "string" + } + }, + "required": [ + "issuer", + "scope", + "jwk", + "expires_at" + ], + "type": "object" + }, + "trustedOAuth2JwtGrantIssuer": { + "description": "OAuth2 JWT Bearer Grant Type Issuer Trust Relationship", + "properties": { + "allow_any_subject": { + "description": "The \"allow_any_subject\" indicates that the issuer is allowed to have any principal as the subject of the JWT.", + "type": "boolean" + }, + "created_at": { + "description": "The \"created_at\" indicates, when grant was created.", + "format": "date-time", + "type": "string" + }, + "expires_at": { + "description": "The \"expires_at\" indicates, when grant will expire, so we will reject assertion from \"issuer\" targeting \"subject\".", + "format": "date-time", + "type": "string" + }, + "id": { + "example": "9edc811f-4e28-453c-9b46-4de65f00217f", + "type": "string" + }, + "issuer": { + "description": "The \"issuer\" identifies the principal that issued the JWT assertion (same as \"iss\" claim in JWT).", + "example": "https://jwt-idp.example.com", + "type": "string" + }, + "public_key": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantJsonWebKey" + }, + "scope": { + "description": "The \"scope\" contains list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749])", + "example": [ + "openid", + "offline" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "subject": { + "description": "The \"subject\" identifies the principal that is the subject of the JWT.", + "example": "mike@example.com", + "type": "string" + } + }, + "type": "object" + }, + "trustedOAuth2JwtGrantIssuers": { + "description": "OAuth2 JWT Bearer Grant Type Issuer Trust Relationships", + "items": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantIssuer" + }, + "type": "array" + }, + "trustedOAuth2JwtGrantJsonWebKey": { + "description": "OAuth2 JWT Bearer Grant Type Issuer Trusted JSON Web Key", + "properties": { + "kid": { + "description": "The \"key_id\" is key unique identifier (same as kid header in jws/jwt).", + "example": "123e4567-e89b-12d3-a456-426655440000", + "type": "string" + }, + "set": { + "description": "The \"set\" is basically a name for a group(set) of keys. Will be the same as \"issuer\" in grant.", + "example": "https://jwt-idp.example.com", + "type": "string" + } + }, + "type": "object" + }, + "unexpectedError": { + "type": "string" + }, + "version": { + "properties": { + "version": { + "description": "Version is the service's version.", + "type": "string" + } + }, + "type": "object" + } + }, + "securitySchemes": { + "basic": { + "scheme": "basic", + "type": "http" + }, + "bearer": { + "scheme": "bearer", + "type": "http" + }, + "oauth2": { + "flows": { + "authorizationCode": { + "authorizationUrl": "https://hydra.demo.ory.sh/oauth2/auth", + "scopes": { + "offline": "A scope required when requesting refresh tokens (alias for `offline_access`)", + "offline_access": "A scope required when requesting refresh tokens", + "openid": "Request an OpenID Connect ID Token" + }, + "tokenUrl": "https://hydra.demo.ory.sh/oauth2/token" + } + }, + "type": "oauth2" + } + } + }, + "info": { + "contact": { + "email": "hi@ory.sh" + }, + "description": "Documentation for all of Ory Hydra's APIs.\n", + "license": { + "name": "Apache 2.0" + }, + "title": "Ory Hydra API", + "version": "" + }, + "openapi": "3.0.3", + "paths": { + "/.well-known/jwks.json": { + "get": { + "description": "This endpoint returns JSON Web Keys required to verifying OpenID Connect ID Tokens and,\nif enabled, OAuth 2.0 JWT Access Tokens. This endpoint can be used with client libraries like\n[node-jwks-rsa](https://github.com/auth0/node-jwks-rsa) among others.", + "operationId": "discoverJsonWebKeys", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Discover Well-Known JSON Web Keys", + "tags": [ + "wellknown" + ] + } + }, + "/.well-known/openid-configuration": { + "get": { + "description": "A mechanism for an OpenID Connect Relying Party to discover the End-User's OpenID Provider and obtain information needed to interact with it, including its OAuth 2.0 endpoint locations.\n\nPopular libraries for OpenID Connect clients include oidc-client-js (JavaScript), go-oidc (Golang), and others.\nFor a full list of clients go here: https://openid.net/developers/certified/", + "operationId": "discoverOidcConfiguration", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oidcConfiguration" + } + } + }, + "description": "oidcConfiguration" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "OpenID Connect Discovery", + "tags": [ + "oidc" + ] + } + }, + "/admin/clients": { + "get": { + "description": "This endpoint lists all clients in the database, and never returns client secrets.\nAs a default it lists the first 100 clients.", + "operationId": "listOAuth2Clients", + "parameters": [ + { + "description": "Items per Page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "in": "query", + "name": "page_size", + "schema": { + "default": 250, + "format": "int64", + "maximum": 500, + "minimum": 1, + "type": "integer" + } + }, + { + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "in": "query", + "name": "page_token", + "schema": { + "default": "1", + "minimum": 1, + "type": "string" + } + }, + { + "description": "The name of the clients to filter by.", + "in": "query", + "name": "client_name", + "schema": { + "type": "string" + } + }, + { + "description": "The owner of the clients to filter by.", + "in": "query", + "name": "owner", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/listOAuth2Clients" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "List OAuth 2.0 Clients", + "tags": [ + "oAuth2" + ] + }, + "post": { + "description": "Create a new OAuth 2.0 client. If you pass `client_secret` the secret is used, otherwise a random secret\nis generated. The secret is echoed in the response. It is not possible to retrieve it later on.", + "operationId": "createOAuth2Client", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "OAuth 2.0 Client Request Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "400": { + "$ref": "#/components/responses/errorOAuth2BadRequest" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Create OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/clients/{id}": { + "delete": { + "description": "Delete an existing OAuth 2.0 Client by its ID.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.\n\nMake sure that this endpoint is well protected and only callable by first-party components.", + "operationId": "deleteOAuth2Client", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Delete OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + }, + "get": { + "description": "Get an OAuth 2.0 client by its ID. This endpoint never returns the client secret.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "getOAuth2Client", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Get an OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + }, + "patch": { + "description": "Patch an existing OAuth 2.0 Client using JSON Patch. If you pass `client_secret`\nthe secret will be updated and returned via the API. This is the\nonly time you will be able to retrieve the client secret, so write it down and keep it safe.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "patchOAuth2Client", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonPatchDocument" + } + } + }, + "description": "OAuth 2.0 Client JSON Patch Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "404": { + "$ref": "#/components/responses/errorOAuth2NotFound" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Patch OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + }, + "put": { + "description": "Replaces an existing OAuth 2.0 Client with the payload you send. If you pass `client_secret` the secret is used,\notherwise the existing secret is used.\n\nIf set, the secret is echoed in the response. It is not possible to retrieve it later on.\n\nOAuth 2.0 Clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "setOAuth2Client", + "parameters": [ + { + "description": "OAuth 2.0 Client ID", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "OAuth 2.0 Client Request Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "400": { + "$ref": "#/components/responses/errorOAuth2BadRequest" + }, + "404": { + "$ref": "#/components/responses/errorOAuth2NotFound" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Set OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/clients/{id}/lifespans": { + "put": { + "description": "Set lifespans of different token types issued for this OAuth 2.0 client. Does not modify other fields.", + "operationId": "setOAuth2ClientLifespans", + "parameters": [ + { + "description": "OAuth 2.0 Client ID", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2ClientTokenLifespans" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Set OAuth2 Client Token Lifespans", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/keys/{set}": { + "delete": { + "description": "Use this endpoint to delete a complete JSON Web Key Set and all the keys in that set.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "deleteJsonWebKeySet", + "parameters": [ + { + "description": "The JSON Web Key Set", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Delete JSON Web Key Set", + "tags": [ + "jwk" + ] + }, + "get": { + "description": "This endpoint can be used to retrieve JWK Sets stored in ORY Hydra.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "getJsonWebKeySet", + "parameters": [ + { + "description": "JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Retrieve a JSON Web Key Set", + "tags": [ + "jwk" + ] + }, + "post": { + "description": "This endpoint is capable of generating JSON Web Key Sets for you. There a different strategies available, such as symmetric cryptographic keys (HS256, HS512) and asymetric cryptographic keys (RS256, ECDSA). If the specified JSON Web Key Set does not exist, it will be created.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "createJsonWebKeySet", + "parameters": [ + { + "description": "The JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createJsonWebKeySet" + } + } + }, + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Create JSON Web Key", + "tags": [ + "jwk" + ] + }, + "put": { + "description": "Use this method if you do not want to let Hydra generate the JWKs for you, but instead save your own.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "setJsonWebKeySet", + "parameters": [ + { + "description": "The JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Update a JSON Web Key Set", + "tags": [ + "jwk" + ] + } + }, + "/admin/keys/{set}/{kid}": { + "delete": { + "description": "Use this endpoint to delete a single JSON Web Key.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A\nJWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses\nthis functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens),\nand allows storing user-defined keys as well.", + "operationId": "deleteJsonWebKey", + "parameters": [ + { + "description": "The JSON Web Key Set", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The JSON Web Key ID (kid)", + "in": "path", + "name": "kid", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Delete JSON Web Key", + "tags": [ + "jwk" + ] + }, + "get": { + "description": "This endpoint returns a singular JSON Web Key contained in a set. It is identified by the set and the specific key ID (kid).", + "operationId": "getJsonWebKey", + "parameters": [ + { + "description": "JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "JSON Web Key ID", + "in": "path", + "name": "kid", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKeySet" + } + } + }, + "description": "jsonWebKeySet" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Get JSON Web Key", + "tags": [ + "jwk" + ] + }, + "put": { + "description": "Use this method if you do not want to let Hydra generate the JWKs for you, but instead save your own.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "operationId": "setJsonWebKey", + "parameters": [ + { + "description": "The JSON Web Key Set ID", + "in": "path", + "name": "set", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "JSON Web Key ID", + "in": "path", + "name": "kid", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKey" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/jsonWebKey" + } + } + }, + "description": "jsonWebKey" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Set JSON Web Key", + "tags": [ + "jwk" + ] + } + }, + "/admin/oauth2/auth/requests/consent": { + "get": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell Ory now about it. If the subject authenticated, he/she must now be asked if\nthe OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the subject's behalf.\n\nThe consent challenge is appended to the consent provider's URL to which the subject's user-agent (browser) is redirected to. The consent\nprovider uses that challenge to fetch information on the OAuth2 request and then tells Ory if the subject accepted\nor rejected the request.\n\nThe default consent provider is available via the Ory Managed Account Experience. To customize the consent provider, please\nhead over to the OAuth 2.0 documentation.", + "operationId": "getOAuth2ConsentRequest", + "parameters": [ + { + "description": "OAuth 2.0 Consent Request Challenge", + "in": "query", + "name": "consent_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2ConsentRequest" + } + } + }, + "description": "oAuth2ConsentRequest" + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Get OAuth 2.0 Consent Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/consent/accept": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell Ory now about it. If the subject authenticated, he/she must now be asked if\nthe OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the subject's behalf.\n\nThe consent challenge is appended to the consent provider's URL to which the subject's user-agent (browser) is redirected to. The consent\nprovider uses that challenge to fetch information on the OAuth2 request and then tells Ory if the subject accepted\nor rejected the request.\n\nThis endpoint tells Ory that the subject has authorized the OAuth 2.0 client to access resources on his/her behalf.\nThe consent provider includes additional information, such as session data for access and ID tokens, and if the\nconsent request should be used as basis for future requests.\n\nThe response contains a redirect URL which the consent provider should redirect the user-agent to.\n\nThe default consent provider is available via the Ory Managed Account Experience. To customize the consent provider, please\nhead over to the OAuth 2.0 documentation.", + "operationId": "acceptOAuth2ConsentRequest", + "parameters": [ + { + "description": "OAuth 2.0 Consent Request Challenge", + "in": "query", + "name": "consent_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/acceptOAuth2ConsentRequest" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Accept OAuth 2.0 Consent Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/consent/reject": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell Ory now about it. If the subject authenticated, he/she must now be asked if\nthe OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the subject's behalf.\n\nThe consent challenge is appended to the consent provider's URL to which the subject's user-agent (browser) is redirected to. The consent\nprovider uses that challenge to fetch information on the OAuth2 request and then tells Ory if the subject accepted\nor rejected the request.\n\nThis endpoint tells Ory that the subject has not authorized the OAuth 2.0 client to access resources on his/her behalf.\nThe consent provider must include a reason why the consent was not granted.\n\nThe response contains a redirect URL which the consent provider should redirect the user-agent to.\n\nThe default consent provider is available via the Ory Managed Account Experience. To customize the consent provider, please\nhead over to the OAuth 2.0 documentation.", + "operationId": "rejectOAuth2ConsentRequest", + "parameters": [ + { + "description": "OAuth 2.0 Consent Request Challenge", + "in": "query", + "name": "consent_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rejectOAuth2Request" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Reject OAuth 2.0 Consent Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/login": { + "get": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell the Ory OAuth2 Service about it.\n\nPer default, the login provider is Ory itself. You may use a different login provider which needs to be a web-app\nyou write and host, and it must be able to authenticate (\"show the subject a login screen\")\na subject (in OAuth2 the proper name for subject is \"resource owner\").\n\nThe authentication challenge is appended to the login provider URL to which the subject's user-agent (browser) is redirected to. The login\nprovider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process.", + "operationId": "getOAuth2LoginRequest", + "parameters": [ + { + "description": "OAuth 2.0 Login Request Challenge", + "in": "query", + "name": "login_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2LoginRequest" + } + } + }, + "description": "oAuth2LoginRequest" + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Get OAuth 2.0 Login Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/login/accept": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell the Ory OAuth2 Service about it.\n\nThe authentication challenge is appended to the login provider URL to which the subject's user-agent (browser) is redirected to. The login\nprovider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process.\n\nThis endpoint tells Ory that the subject has successfully authenticated and includes additional information such as\nthe subject's ID and if Ory should remember the subject's subject agent for future authentication attempts by setting\na cookie.\n\nThe response contains a redirect URL which the login provider should redirect the user-agent to.", + "operationId": "acceptOAuth2LoginRequest", + "parameters": [ + { + "description": "OAuth 2.0 Login Request Challenge", + "in": "query", + "name": "login_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/acceptOAuth2LoginRequest" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Accept OAuth 2.0 Login Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/login/reject": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, Ory asks the login provider\nto authenticate the subject and then tell the Ory OAuth2 Service about it.\n\nThe authentication challenge is appended to the login provider URL to which the subject's user-agent (browser) is redirected to. The login\nprovider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process.\n\nThis endpoint tells Ory that the subject has not authenticated and includes a reason why the authentication\nwas denied.\n\nThe response contains a redirect URL which the login provider should redirect the user-agent to.", + "operationId": "rejectOAuth2LoginRequest", + "parameters": [ + { + "description": "OAuth 2.0 Login Request Challenge", + "in": "query", + "name": "login_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rejectOAuth2Request" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Reject OAuth 2.0 Login Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/logout": { + "get": { + "description": "Use this endpoint to fetch an Ory OAuth 2.0 logout request.", + "operationId": "getOAuth2LogoutRequest", + "parameters": [ + { + "in": "query", + "name": "logout_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2LogoutRequest" + } + } + }, + "description": "oAuth2LogoutRequest" + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Get OAuth 2.0 Session Logout Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/logout/accept": { + "put": { + "description": "When a user or an application requests Ory OAuth 2.0 to remove the session state of a subject, this endpoint is used to confirm that logout request.\n\nThe response contains a redirect URL which the consent provider should redirect the user-agent to.", + "operationId": "acceptOAuth2LogoutRequest", + "parameters": [ + { + "description": "OAuth 2.0 Logout Request Challenge", + "in": "query", + "name": "logout_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2RedirectTo" + } + } + }, + "description": "oAuth2RedirectTo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Accept OAuth 2.0 Session Logout Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/requests/logout/reject": { + "put": { + "description": "When a user or an application requests Ory OAuth 2.0 to remove the session state of a subject, this endpoint is used to deny that logout request.\nNo HTTP request body is required.\n\nThe response is empty as the logout provider has to chose what action to perform next.", + "operationId": "rejectOAuth2LogoutRequest", + "parameters": [ + { + "in": "query", + "name": "logout_challenge", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Reject OAuth 2.0 Session Logout Request", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/sessions/consent": { + "delete": { + "description": "This endpoint revokes a subject's granted consent sessions and invalidates all\nassociated OAuth 2.0 Access Tokens. You may also only revoke sessions for a specific OAuth 2.0 Client ID.", + "operationId": "revokeOAuth2ConsentSessions", + "parameters": [ + { + "description": "OAuth 2.0 Consent Subject\n\nThe subject whose consent sessions should be deleted.", + "in": "query", + "name": "subject", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "OAuth 2.0 Client ID\n\nIf set, deletes only those consent sessions that have been granted to the specified OAuth 2.0 Client ID.", + "in": "query", + "name": "client", + "schema": { + "type": "string" + } + }, + { + "description": "Revoke All Consent Sessions\n\nIf set to `true` deletes all consent sessions by the Subject that have been granted.", + "in": "query", + "name": "all", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Revoke OAuth 2.0 Consent Sessions of a Subject", + "tags": [ + "oAuth2" + ] + }, + "get": { + "description": "This endpoint lists all subject's granted consent sessions, including client and granted scope.\nIf the subject is unknown or has not granted any consent sessions yet, the endpoint returns an\nempty JSON array with status code 200 OK.", + "operationId": "listOAuth2ConsentSessions", + "parameters": [ + { + "description": "Items per Page\n\nThis is the number of items per page to return.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "in": "query", + "name": "page_size", + "schema": { + "default": 250, + "format": "int64", + "maximum": 500, + "minimum": 1, + "type": "integer" + } + }, + { + "description": "Next Page Token\n\nThe next page token.\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).", + "in": "query", + "name": "page_token", + "schema": { + "default": "1", + "minimum": 1, + "type": "string" + } + }, + { + "description": "The subject to list the consent sessions for.", + "in": "query", + "name": "subject", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The login session id to list the consent sessions for.", + "in": "query", + "name": "login_session_id", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2ConsentSessions" + } + } + }, + "description": "oAuth2ConsentSessions" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "List OAuth 2.0 Consent Sessions of a Subject", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/auth/sessions/login": { + "delete": { + "description": "This endpoint invalidates authentication sessions. After revoking the authentication session(s), the subject\nhas to re-authenticate at the Ory OAuth2 Provider. This endpoint does not invalidate any tokens.\n\nIf you send the subject in a query param, all authentication sessions that belong to that subject are revoked.\nNo OpennID Connect Front- or Back-channel logout is performed in this case.\n\nAlternatively, you can send a SessionID via `sid` query param, in which case, only the session that is connected\nto that SessionID is revoked. OpenID Connect Back-channel logout is performed in this case.", + "operationId": "revokeOAuth2LoginSessions", + "parameters": [ + { + "description": "OAuth 2.0 Subject\n\nThe subject to revoke authentication sessions for.", + "in": "query", + "name": "subject", + "schema": { + "type": "string" + } + }, + { + "description": "OAuth 2.0 Subject\n\nThe subject to revoke authentication sessions for.", + "in": "query", + "name": "sid", + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Revokes OAuth 2.0 Login Sessions by either a Subject or a SessionID", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/introspect": { + "post": { + "description": "The introspection endpoint allows to check if a token (both refresh and access) is active or not. An active token\nis neither expired nor revoked. If a token is active, additional information on the token will be included. You can\nset additional data for a token by setting `session.access_token` during the consent flow.", + "operationId": "introspectOAuth2Token", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "properties": { + "scope": { + "description": "An optional, space separated list of required scopes. If the access token was not granted one of the\nscopes, the result of active will be false.", + "type": "string", + "x-formData-name": "scope" + }, + "token": { + "description": "The string value of the token. For access tokens, this\nis the \"access_token\" value returned from the token endpoint\ndefined in OAuth 2.0. For refresh tokens, this is the \"refresh_token\"\nvalue returned.", + "required": [ + "token" + ], + "type": "string", + "x-formData-name": "token" + } + }, + "required": [ + "token" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/introspectedOAuth2Token" + } + } + }, + "description": "introspectedOAuth2Token" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Introspect OAuth2 Access and Refresh Tokens", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/oauth2/tokens": { + "delete": { + "description": "This endpoint deletes OAuth2 access tokens issued to an OAuth 2.0 Client from the database.", + "operationId": "deleteOAuth2Token", + "parameters": [ + { + "description": "OAuth 2.0 Client ID", + "in": "query", + "name": "client_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "Delete OAuth 2.0 Access Tokens from specific OAuth 2.0 Client", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/trust/grants/jwt-bearer/issuers": { + "get": { + "description": "Use this endpoint to list all trusted JWT Bearer Grant Type Issuers.", + "operationId": "listTrustedOAuth2JwtGrantIssuers", + "parameters": [ + { + "in": "query", + "name": "MaxItems", + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "in": "query", + "name": "DefaultItems", + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "If optional \"issuer\" is supplied, only jwt-bearer grants with this issuer will be returned.", + "in": "query", + "name": "issuer", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantIssuers" + } + } + }, + "description": "trustedOAuth2JwtGrantIssuers" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "List Trusted OAuth2 JWT Bearer Grant Type Issuers", + "tags": [ + "oAuth2" + ] + }, + "post": { + "description": "Use this endpoint to establish a trust relationship for a JWT issuer\nto perform JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication\nand Authorization Grants [RFC7523](https://datatracker.ietf.org/doc/html/rfc7523).", + "operationId": "trustOAuth2JwtGrantIssuer", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/trustOAuth2JwtGrantIssuer" + } + } + }, + "x-originalParamName": "Body" + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantIssuer" + } + } + }, + "description": "trustedOAuth2JwtGrantIssuer" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Trust OAuth2 JWT Bearer Grant Type Issuer", + "tags": [ + "oAuth2" + ] + } + }, + "/admin/trust/grants/jwt-bearer/issuers/{id}": { + "delete": { + "description": "Use this endpoint to delete trusted JWT Bearer Grant Type Issuer. The ID is the one returned when you\ncreated the trust relationship.\n\nOnce deleted, the associated issuer will no longer be able to perform the JSON Web Token (JWT) Profile\nfor OAuth 2.0 Client Authentication and Authorization Grant.", + "operationId": "deleteTrustedOAuth2JwtGrantIssuer", + "parameters": [ + { + "description": "The id of the desired grant", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Delete Trusted OAuth2 JWT Bearer Grant Type Issuer", + "tags": [ + "oAuth2" + ] + }, + "get": { + "description": "Use this endpoint to get a trusted JWT Bearer Grant Type Issuer. The ID is the one returned when you\ncreated the trust relationship.", + "operationId": "getTrustedOAuth2JwtGrantIssuer", + "parameters": [ + { + "description": "The id of the desired grant", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/trustedOAuth2JwtGrantIssuer" + } + } + }, + "description": "trustedOAuth2JwtGrantIssuer" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Get Trusted OAuth2 JWT Bearer Grant Type Issuer", + "tags": [ + "oAuth2" + ] + } + }, + "/health/alive": { + "get": { + "description": "This endpoint returns a HTTP 200 status code when Ory Hydra is accepting incoming\nHTTP requests. This status does currently not include checks whether the database connection is working.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the health status will never\nrefer to the cluster state, only to a single instance.", + "operationId": "isAlive", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/healthStatus" + } + } + }, + "description": "Ory Hydra is ready to accept connections." + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "summary": "Check HTTP Server Status", + "tags": [ + "metadata" + ] + } + }, + "/health/ready": { + "get": { + "description": "This endpoint returns a HTTP 200 status code when Ory Hydra is up running and the environment dependencies (e.g.\nthe database) are responsive as well.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of Ory Hydra, the health status will never\nrefer to the cluster state, only to a single instance.", + "operationId": "isReady", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "description": "Always \"ok\".", + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "Ory Hydra is ready to accept requests." + }, + "503": { + "content": { + "application/json": { + "schema": { + "properties": { + "errors": { + "additionalProperties": { + "type": "string" + }, + "description": "Errors contains a list of errors that caused the not ready status.", + "type": "object" + } + }, + "type": "object" + } + } + }, + "description": "Ory Kratos is not yet ready to accept requests." + } + }, + "summary": "Check HTTP Server and Database Status", + "tags": [ + "metadata" + ] + } + }, + "/oauth2/auth": { + "get": { + "description": "Use open source libraries to perform OAuth 2.0 and OpenID Connect\navailable for any programming language. You can find a list of libraries at https://oauth.net/code/\n\nThe Ory SDK is not yet able to this endpoint properly.", + "operationId": "oAuth2Authorize", + "responses": { + "302": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "OAuth 2.0 Authorize Endpoint", + "tags": [ + "oAuth2" + ] + } + }, + "/oauth2/register": { + "post": { + "description": "This endpoint behaves like the administrative counterpart (`createOAuth2Client`) but is capable of facing the\npublic internet directly and can be used in self-service. It implements the OpenID Connect\nDynamic Client Registration Protocol. This feature needs to be enabled in the configuration. This endpoint\nis disabled by default. It can be enabled by an administrator.\n\nPlease note that using this endpoint you are not able to choose the `client_secret` nor the `client_id` as those\nvalues will be server generated when specifying `token_endpoint_auth_method` as `client_secret_basic` or\n`client_secret_post`.\n\nThe `client_secret` will be returned in the response and you will not be able to retrieve it later on.\nWrite the secret down and keep it somewhere safe.", + "operationId": "createOidcDynamicClient", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "Dynamic Client Registration Request Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "400": { + "$ref": "#/components/responses/errorOAuth2BadRequest" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "summary": "Register OAuth2 Client using OpenID Dynamic Client Registration", + "tags": [ + "oidc" + ] + } + }, + "/oauth2/register/{id}": { + "delete": { + "description": "This endpoint behaves like the administrative counterpart (`deleteOAuth2Client`) but is capable of facing the\npublic internet directly and can be used in self-service. It implements the OpenID Connect\nDynamic Client Registration Protocol. This feature needs to be enabled in the configuration. This endpoint\nis disabled by default. It can be enabled by an administrator.\n\nTo use this endpoint, you will need to present the client's authentication credentials. If the OAuth2 Client\nuses the Token Endpoint Authentication Method `client_secret_post`, you need to present the client secret in the URL query.\nIf it uses `client_secret_basic`, present the Client ID and the Client Secret in the Authorization header.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "deleteOidcDynamicClient", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/genericError" + } + } + }, + "description": "genericError" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Delete OAuth 2.0 Client using the OpenID Dynamic Client Registration Management Protocol", + "tags": [ + "oidc" + ] + }, + "get": { + "description": "This endpoint behaves like the administrative counterpart (`getOAuth2Client`) but is capable of facing the\npublic internet directly and can be used in self-service. It implements the OpenID Connect\nDynamic Client Registration Protocol.\n\nTo use this endpoint, you will need to present the client's authentication credentials. If the OAuth2 Client\nuses the Token Endpoint Authentication Method `client_secret_post`, you need to present the client secret in the URL query.\nIf it uses `client_secret_basic`, present the Client ID and the Client Secret in the Authorization header.", + "operationId": "getOidcDynamicClient", + "parameters": [ + { + "description": "The id of the OAuth 2.0 Client.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Get OAuth2 Client using OpenID Dynamic Client Registration", + "tags": [ + "oidc" + ] + }, + "put": { + "description": "This endpoint behaves like the administrative counterpart (`setOAuth2Client`) but is capable of facing the\npublic internet directly to be used by third parties. It implements the OpenID Connect\nDynamic Client Registration Protocol.\n\nThis feature is disabled per default. It can be enabled by a system administrator.\n\nIf you pass `client_secret` the secret is used, otherwise the existing secret is used. If set, the secret is echoed in the response.\nIt is not possible to retrieve it later on.\n\nTo use this endpoint, you will need to present the client's authentication credentials. If the OAuth2 Client\nuses the Token Endpoint Authentication Method `client_secret_post`, you need to present the client secret in the URL query.\nIf it uses `client_secret_basic`, present the Client ID and the Client Secret in the Authorization header.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are\ngenerated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.", + "operationId": "setOidcDynamicClient", + "parameters": [ + { + "description": "OAuth 2.0 Client ID", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "OAuth 2.0 Client Request Body", + "required": true, + "x-originalParamName": "Body" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2Client" + } + } + }, + "description": "oAuth2Client" + }, + "404": { + "$ref": "#/components/responses/errorOAuth2NotFound" + }, + "default": { + "$ref": "#/components/responses/errorOAuth2Default" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Set OAuth2 Client using OpenID Dynamic Client Registration", + "tags": [ + "oidc" + ] + } + }, + "/oauth2/revoke": { + "post": { + "description": "Revoking a token (both access and refresh) means that the tokens will be invalid. A revoked access token can no\nlonger be used to make access requests, and a revoked refresh token can no longer be used to refresh an access token.\nRevoking a refresh token also invalidates the access token that was created with it. A token may only be revoked by\nthe client the token was generated for.", + "operationId": "revokeOAuth2Token", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "properties": { + "client_id": { + "type": "string", + "x-formData-name": "client_id" + }, + "client_secret": { + "type": "string", + "x-formData-name": "client_secret" + }, + "token": { + "required": [ + "token" + ], + "type": "string", + "x-formData-name": "token" + } + }, + "required": [ + "token" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "$ref": "#/components/responses/emptyResponse" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "security": [ + { + "basic": [] + }, + { + "oauth2": [] + } + ], + "summary": "Revoke OAuth 2.0 Access or Refresh Token", + "tags": [ + "oAuth2" + ] + } + }, + "/oauth2/sessions/logout": { + "get": { + "description": "This endpoint initiates and completes user logout at the Ory OAuth2 & OpenID provider and initiates OpenID Connect Front- / Back-channel logout:\n\nhttps://openid.net/specs/openid-connect-frontchannel-1_0.html\nhttps://openid.net/specs/openid-connect-backchannel-1_0.html\n\nBack-channel logout is performed asynchronously and does not affect logout flow.", + "operationId": "revokeOidcSession", + "responses": { + "302": { + "$ref": "#/components/responses/emptyResponse" + } + }, + "summary": "OpenID Connect Front- and Back-channel Enabled Logout", + "tags": [ + "oidc" + ] + } + }, + "/oauth2/token": { + "post": { + "description": "Use open source libraries to perform OAuth 2.0 and OpenID Connect\navailable for any programming language. You can find a list of libraries here https://oauth.net/code/\n\nThe Ory SDK is not yet able to this endpoint properly.", + "operationId": "oauth2TokenExchange", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "properties": { + "client_id": { + "type": "string", + "x-formData-name": "client_id" + }, + "code": { + "type": "string", + "x-formData-name": "code" + }, + "grant_type": { + "required": [ + "grant_type" + ], + "type": "string", + "x-formData-name": "grant_type" + }, + "redirect_uri": { + "type": "string", + "x-formData-name": "redirect_uri" + }, + "refresh_token": { + "type": "string", + "x-formData-name": "refresh_token" + } + }, + "required": [ + "grant_type" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oAuth2TokenExchange" + } + } + }, + "description": "oAuth2TokenExchange" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "security": [ + { + "basic": [] + }, + { + "oauth2": [] + } + ], + "summary": "The OAuth 2.0 Token Endpoint", + "tags": [ + "oAuth2" + ] + } + }, + "/userinfo": { + "get": { + "description": "This endpoint returns the payload of the ID Token, including `session.id_token` values, of\nthe provided OAuth 2.0 Access Token's consent request.\n\nIn the case of authentication error, a WWW-Authenticate header might be set in the response\nwith more information about the error. See [the spec](https://datatracker.ietf.org/doc/html/rfc6750#section-3)\nfor more details about header format.", + "operationId": "getOidcUserInfo", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/oidcUserInfo" + } + } + }, + "description": "oidcUserInfo" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "security": [ + { + "oauth2": [] + } + ], + "summary": "OpenID Connect Userinfo", + "tags": [ + "oidc" + ] + } + }, + "/version": { + "get": { + "description": "This endpoint returns the version of Ory Hydra.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the version will never\nrefer to the cluster state, only to a single instance.", + "operationId": "getVersion", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "version": { + "description": "The version of Ory Hydra.", + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "Returns the Ory Hydra version." + } + }, + "summary": "Return Running Software Version.", + "tags": [ + "metadata" + ] + } + } + }, + "tags": [ + { + "description": "OAuth 2.0", + "name": "oAuth2" + }, + { + "description": "OpenID Connect", + "name": "oidc" + }, + { + "description": "JSON Web Keys", + "name": "jwk" + }, + { + "description": "Well-Known Endpoints", + "name": "wellknown" + }, + { + "description": "Service Metadata", + "name": "metadata" + } + ], + "x-forwarded-proto": "string", + "x-request-id": "string" +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/test/resources/resource-server-open-api-v3.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/test/resources/resource-server-open-api-v3.json new file mode 100644 index 0000000..d475800 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/src/test/resources/resource-server-open-api-v3.json @@ -0,0 +1,115 @@ +{ + "openapi": "3.0.1", + "info": { + "description": "Example resource server" + }, + "servers": [ + { + "url": "https://resource-server.com/api", + "description": "Generated server url" + } + ], + "security": [ + { + "Introspect OAuth 2.0": [] + } + ], + "paths": { + "/calculate": { + "put": { + "tags": [ + "calculate-controller" + ], + "summary": "Some calculate", + "operationId": "calculate", + "responses": { + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "500": { + "description": "Internal Server Error" + }, + "200": { + "description": "OK" + } + } + } + }, + "/statistics": { + "get": { + "tags": [ + "statistics-controller" + ], + "summary": "Get statistics", + "operationId": "getStatistics", + "responses": { + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "500": { + "description": "Internal Server Error" + }, + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Point" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Point": { + "type": "object", + "properties": { + "x": { + "type": "number", + "format": "double" + }, + "y": { + "type": "number", + "format": "double" + } + }, + "description": "Graph point" + } + }, + "securitySchemes": { + "Introspect OAuth 2.0": { + "type": "http", + "in": "header", + "scheme": "bearer", + "bearerFormat": "opaque token" + } + } + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/test_and_report.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/test_and_report.bash new file mode 100644 index 0000000..ef6d5fe --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-backend/test_and_report.bash @@ -0,0 +1,3 @@ +#!/bin/bash + +./gradlew test jacocoTestReport diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/.editorconfig b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/.editorconfig new file mode 100644 index 0000000..59d9a3a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/.gitignore b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/.gitignore new file mode 100644 index 0000000..1f4031f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/.gitignore @@ -0,0 +1,44 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings + +# System files +.DS_Store +Thumbs.db + +package-lock.json diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/angular.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/angular.json new file mode 100644 index 0000000..18fa92b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/angular.json @@ -0,0 +1,121 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "cli": { + "analytics": false + }, + "projects": { + "write-and-read-frontend": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:application", + "options": { + "outputPath": "dist/write-and-read-frontend", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": [ + "zone.js" + ], + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "src/favicon.ico", + "src/assets", + "src/fonts" + ], + "styles": [ + "src/styles.scss" + ], + "scripts": [], + "server": "src/main.server.ts", + "prerender": true, + "ssr": { + "entry": "server.ts" + } + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "write-and-read-frontend:build:production" + }, + "development": { + "buildTarget": "write-and-read-frontend:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "write-and-read-frontend:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "polyfills": [ + "zone.js", + "zone.js/testing" + ], + "tsConfig": "tsconfig.spec.json", + "inlineStyleLanguage": "scss", + "assets": [ + "src/favicon.ico", + "src/assets", + "src/fonts" + ], + "styles": [ + "src/styles.scss" + ], + "scripts": [], + "karmaConfig": "karma.conf.js", + "codeCoverageExclude": [ + "src/app/window.service.ts" + ] + } + } + } + } + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/build_image.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/build_image.bash new file mode 100644 index 0000000..0d49d90 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/build_image.bash @@ -0,0 +1,47 @@ +#!/bin/bash + +# export HTTP_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +# export HTTPS_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +# export NO_PROXY="localhost,127.0.0.1" + +export REPO_IMAGE="chistousov" +export PROJECT_NAME="ory-hydra-oauth2-example-client-write-and-read-frontend" +export VERSION="1.0.0" + +# install pack +# https://buildpacks.io/docs/tools/pack/#linux-script-install +# (curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.29.0/pack-v0.29.0-linux.tgz" | sudo tar -C /usr/local/bin/ --no-same-owner -xzv pack) + +npm i + +ng test --no-watch --code-coverage --browsers Firefox +rm -rf dist/ || true +npm run build + +docker pull paketobuildpacks/builder-jammy-full:0.3.316 + +rm -rf app_server/ || true +mkdir app_server +mv dist/ app_server/dist + +pack -v \ +--path app_server \ +build \ +$REPO_IMAGE/$PROJECT_NAME:$VERSION \ +--env HTTP_PROXY="$HTTP_PROXY" \ +--env HTTPS_PROXY="$HTTPS_PROXY" \ +--env NO_PROXY="$NO_PROXY" \ +--env BP_NODE_OPTIMIZE_MEMORY=true \ +--env BP_HEALTH_CHECKER_ENABLED=true \ +--env BP_LAUNCHPOINT="dist/write-and-read-frontend/server/server.mjs" \ +--buildpack gcr.io/paketo-buildpacks/nodejs \ +--buildpack gcr.io/paketo-buildpacks/health-checker:latest \ +--builder paketobuildpacks/builder-jammy-full:0.3.316 + +rm -rf app_server + +# publish in docker hub +# docker login +# docker push $REPO_IMAGE/$PROJECT_NAME:$VERSION +# docker logout +# docker rmi $REPO_IMAGE/$PROJECT_NAME:$VERSION diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/karma.conf.js b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/karma.conf.js new file mode 100644 index 0000000..6efc6b4 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/karma.conf.js @@ -0,0 +1,40 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma'), + require("karma-firefox-launcher") + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, './coverage/write-and-read-frontend'), + subdir: '.', + reporters: [ + { type: 'html' }, + { type: 'text-summary' } + ] + }, + reporters: ['progress', 'kjhtml'], + browsers: ['Firefox'], + restartOnFileChange: true + }); +}; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/mock-data.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/mock-data.json new file mode 100644 index 0000000..48c67be --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/mock-data.json @@ -0,0 +1,16 @@ +{ + "data":[ + { + "x": 1, + "y": 2.3 + }, + { + "x": 2, + "y": 5.6 + }, + { + "x": 3, + "y": 4.7 + } + ] + } diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/mock-server.js b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/mock-server.js new file mode 100644 index 0000000..bb6369b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/mock-server.js @@ -0,0 +1,28 @@ +const jsonServer = require('json-server'); +const server = jsonServer.create(); +const router = jsonServer.router('mock-data.json'); +const middlewares = jsonServer.defaults(); + +server.use(middlewares); + +server.get('/logged-in', (req, res) => { + res.status(200).jsonp({ + redirect_to: "/somesome" + }) +}) + +server.get('/somesome', (req, res) => { + res.status(200).jsonp({ + "test": "aaaaaaaaaaaaaaaaaaaaaaaa!" + }) +}) + +server.put('/change', (req, res) => { + res.status(200).jsonp({}) +}) + +server.use(router); + +server.listen(3000, () => { + console.log('JSON Server is running'); +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/package.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/package.json new file mode 100644 index 0000000..b70db39 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/package.json @@ -0,0 +1,49 @@ +{ + "name": "write-and-read-frontend", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test", + "dev:mock": "node mock-server.js", + "serve:ssr:write-and-read-frontend": "ng build --configuration development && node dist/write-and-read-frontend/server/server.mjs" + }, + "private": true, + "dependencies": { + "@angular/animations": "^17.0.0", + "@angular/cdk": "^17.0.1", + "@angular/common": "^17.0.0", + "@angular/compiler": "^17.0.0", + "@angular/core": "^17.0.0", + "@angular/forms": "^17.0.0", + "@angular/material": "^17.0.1", + "@angular/platform-browser": "^17.0.0", + "@angular/platform-browser-dynamic": "^17.0.0", + "@angular/platform-server": "^17.0.0", + "@angular/router": "^17.0.0", + "@angular/ssr": "^17.0.1", + "express": "^4.18.2", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.14.2" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^17.0.1", + "@angular/cli": "^17.0.1", + "@angular/compiler-cli": "^17.0.0", + "@types/express": "^4.17.17", + "@types/jasmine": "~5.1.0", + "@types/node": "^18.18.0", + "jasmine-core": "~5.1.0", + "json-server": "^0.17.4", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-firefox-launcher": "^2.1.2", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.2.2" + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/server.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/server.ts new file mode 100644 index 0000000..96014ed --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/server.ts @@ -0,0 +1,60 @@ +import { APP_BASE_HREF } from '@angular/common'; +import { CommonEngine } from '@angular/ssr'; +import express from 'express'; +import { fileURLToPath } from 'node:url'; +import { dirname, join, resolve } from 'node:path'; +import bootstrap from './src/main.server'; + +// The Express app is exported so that it can be used by serverless Functions. +export function app(): express.Express { + const server = express(); + const serverDistFolder = dirname(fileURLToPath(import.meta.url)); + const browserDistFolder = resolve(serverDistFolder, '../browser'); + const indexHtml = join(serverDistFolder, 'index.server.html'); + + const commonEngine = new CommonEngine(); + + server.set('view engine', 'html'); + server.set('views', browserDistFolder); + + server.get('/health', (req, res) => { + res.status(200).send('Ok'); + }); + + // Example Express Rest API endpoints + // server.get('/api/**', (req, res) => { }); + // Serve static files from /browser + server.get('*.*', express.static(browserDistFolder, { + maxAge: '1y' + })); + + // All regular routes use the Angular engine + server.get('*', (req, res, next) => { + const { protocol, originalUrl, baseUrl, headers } = req; + + commonEngine + .render({ + bootstrap, + documentFilePath: indexHtml, + url: `${protocol}://${headers.host}${originalUrl}`, + publicPath: browserDistFolder, + providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], + }) + .then((html) => res.send(html)) + .catch((err) => next(err)); + }); + + return server; +} + +function run(): void { + const port = process.env['PORT'] || 4000; + + // Start up the Node server + const server = app(); + server.listen(port, () => { + console.log(`Node Express server listening on http://localhost:${port}`); + }); +} + +run(); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/add-point/add-point.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/add-point/add-point.component.html new file mode 100644 index 0000000..6dee397 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/add-point/add-point.component.html @@ -0,0 +1,11 @@ +
+ +
+ + + + \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/add-point/add-point.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/add-point/add-point.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/add-point/add-point.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/add-point/add-point.component.spec.ts new file mode 100644 index 0000000..68127b8 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/add-point/add-point.component.spec.ts @@ -0,0 +1,105 @@ +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; + +import { AddPointComponent } from './add-point.component'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { GenerationCookieCsrfService } from '../generation-cookie-csrf.service'; +import { AppService } from '../app.service'; +import { DebugElement } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { of, throwError } from 'rxjs'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { HttpErrorResponse } from '@angular/common/http'; + +describe('AddPointComponent', () => { + let generationCookieCsrfServiceSpy: jasmine.SpyObj; + let appServiceSpy: jasmine.SpyObj; + + let component: AddPointComponent; + let fixture: ComponentFixture; + + let hostDe: DebugElement; + + let addButton: HTMLButtonElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + AddPointComponent, + HttpClientTestingModule, + NoopAnimationsModule + ], + providers: [ + { + provide: GenerationCookieCsrfService, + useValue: jasmine.createSpyObj('GenerationCookieCsrfService', ['generateCookieCsrf']) + }, + { + provide: AppService, + useValue: jasmine.createSpyObj('AppService', ['change']) + } + ] + }) + .compileComponents() + + }); + + beforeEach(() => { + generationCookieCsrfServiceSpy = TestBed.inject(GenerationCookieCsrfService) as jasmine.SpyObj; + appServiceSpy = TestBed.inject(AppService) as jasmine.SpyObj; + + fixture = TestBed.createComponent(AddPointComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + + hostDe = fixture.debugElement; + + addButton = hostDe.query(By.css("#addButton")).nativeElement; + }); + + + + it('addValue should be succeed', fakeAsync(() => { + // given (instead of when) + + appServiceSpy.change.and.returnValue(of(1)); + + // when + + addButton.click(); + tick(10000); + + // then (instead of verify) + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(appServiceSpy.change.calls.count()) + .withContext("appServiceSpy.change.calls.count() !== 1") + .toBe(1); + + })); + + it('addValue should be fail (enter)', fakeAsync(() => { + // given (instead of when) + + appServiceSpy.change.and.returnValue(throwError(() => new HttpErrorResponse({status: 404}))); + + // when + + window.dispatchEvent(new KeyboardEvent('keydown',{ + key: 'enter' + })); + + // then (instead of verify) + + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext("generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() !== 1") + .toBe(1); + + expect(appServiceSpy.change.calls.count()) + .withContext("appServiceSpy.change.calls.count() !== 1") + .toBe(1); + })); +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/add-point/add-point.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/add-point/add-point.component.ts new file mode 100644 index 0000000..c7e6dc5 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/add-point/add-point.component.ts @@ -0,0 +1,83 @@ +import { Component, HostListener, OnDestroy } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { Subject, first, takeUntil } from 'rxjs'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { CommonModule } from '@angular/common'; +import { AppService } from '../app.service'; +import { HttpErrorResponse } from '@angular/common/http'; +import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar'; +import { GenerationCookieCsrfService } from '../generation-cookie-csrf.service'; + +@Component({ + selector: 'app-add-point', + standalone: true, + imports: [ + CommonModule, + MatButtonModule, + MatTooltipModule, + MatProgressSpinnerModule + ], + templateUrl: './add-point.component.html', + styleUrl: './add-point.component.scss' +}) +export class AddPointComponent implements OnDestroy { + // to clear the rxjs memory + // для очистки памяти rxjs + private readonly onDestroy = new Subject(); + + // waiting for a response from the server + // ожидание ответа от сервера + getResponseFromServerFlag: boolean = false; + + readonly actionCloseButtonName = 'CLOSE'; + readonly confOptionsSnackbar: MatSnackBarConfig = { + duration: 5000 + }; + + @HostListener('window:keydown.enter', ['$event']) onKeydownEnterHandler(event: KeyboardEvent) { + this.addValue(); + } + + constructor( + private appService: AppService, + private matSnackBar: MatSnackBar, + private generationCookieCsrfService: GenerationCookieCsrfService){} + + addValue(){ + + if (!this.getResponseFromServerFlag) { + + this.getResponseFromServerFlag = true; + + this.generationCookieCsrfService.generateCookieCsrf(); + + this.appService.change() + .pipe( + first(), + takeUntil(this.onDestroy) + ) + .subscribe({ + next: (el) => { + + this.matSnackBar.open("Success", this.actionCloseButtonName, this.confOptionsSnackbar); + + // ответ получили + this.getResponseFromServerFlag = false; + }, + error: (error: HttpErrorResponse) => { + + // ответ получили + this.getResponseFromServerFlag = false; + } + }); + + } + } + + + ngOnDestroy(): void { + this.onDestroy.next(); + this.onDestroy.complete(); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.component.html new file mode 100644 index 0000000..a693b2b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.component.html @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.component.spec.ts new file mode 100644 index 0000000..e375e8a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.component.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from '@angular/core/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AppComponent], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.component.ts new file mode 100644 index 0000000..ba49a19 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.component.ts @@ -0,0 +1,13 @@ +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + selector: 'app-root', + standalone: true, + imports: [CommonModule, RouterOutlet], + templateUrl: './app.component.html', + styleUrl: './app.component.scss' +}) +export class AppComponent { +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.config.server.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.config.server.ts new file mode 100644 index 0000000..b4d57c9 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.config.server.ts @@ -0,0 +1,11 @@ +import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; +import { provideServerRendering } from '@angular/platform-server'; +import { appConfig } from './app.config'; + +const serverConfig: ApplicationConfig = { + providers: [ + provideServerRendering() + ] +}; + +export const config = mergeApplicationConfig(appConfig, serverConfig); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.config.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.config.ts new file mode 100644 index 0000000..a4e75e9 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.config.ts @@ -0,0 +1,16 @@ +import { ApplicationConfig, importProvidersFrom } from '@angular/core'; +import { provideRouter } from '@angular/router'; + +import { routes } from './app.routes'; +import { provideClientHydration } from '@angular/platform-browser'; +import { provideAnimations } from '@angular/platform-browser/animations'; +import { HttpClientModule } from '@angular/common/http'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideRouter(routes), + provideClientHydration(), + importProvidersFrom(HttpClientModule), + provideAnimations() + ] +}; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.routes.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.routes.ts new file mode 100644 index 0000000..52e40bb --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.routes.ts @@ -0,0 +1,28 @@ +import { Routes } from '@angular/router'; +import { NotFoundComponent } from './not-found/not-found.component'; +import { ErrorComponent } from './error/error.component'; +import { LayoutComponent } from './layout/layout.component'; +import { authGuard } from './auth.guard'; +import { AddPointComponent } from './add-point/add-point.component'; + +export const routes: Routes = [ + { + path: 'error', + component: ErrorComponent + }, + { + path: '', + component: LayoutComponent, + canActivate: [authGuard], + children: [ + { + path: '', + component: AddPointComponent + } + ] + }, + { + path: '**', + component: NotFoundComponent + }, +]; \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.service.spec.ts new file mode 100644 index 0000000..333e993 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.service.spec.ts @@ -0,0 +1,97 @@ +import { TestBed } from '@angular/core/testing'; + +import { AppService } from './app.service'; + +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { environment } from '../environments/environment'; +import { HttpErrorResponse } from '@angular/common/http'; +import { ErrorService } from './error.service'; + +describe('AppService', () => { + let service: AppService; + let errorServiceSpy: jasmine.SpyObj; + let httpTestingController: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ HttpClientTestingModule ], + providers: [ + { + provide: ErrorService, useValue: jasmine.createSpyObj('ErrorService', ['handle']) + } + ] + }); + service = TestBed.inject(AppService); + errorServiceSpy = TestBed.inject(ErrorService) as jasmine.SpyObj; + httpTestingController = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpTestingController.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('change', (done: DoneFn) => { + // given (instead of when) + + const changeHttpURL = `${environment.apiUrl}/change`; + + // when + + service.change() + .subscribe({ + next: ps => done(), + error: er => done.fail('change is expected') + }); + + const req = httpTestingController.expectOne(changeHttpURL); + req.flush({}); + + // then (instead of verify) + + expect(req.request.method).toEqual('PUT'); + }); + + it('change (fail)', done => { + // given (instead of when) + + const changeHttpURL = `${environment.apiUrl}/change`; + + const expectedErrorResponse = new HttpErrorResponse({ + error: '["password is invalid"]', + url: changeHttpURL, + status: 400, + statusText: 'Bad request' + }); + + // when + + service.change() + .subscribe({ + next: ps => done.fail('change not expected'), + error: (actualErrorResponse: HttpErrorResponse) => { + expect(actualErrorResponse).toEqual(expectedErrorResponse); + done(); + } + }); + + + const req = httpTestingController.expectOne(changeHttpURL); + + req.flush(expectedErrorResponse.error, expectedErrorResponse); + + // then (instead of verify) + + expect(req.request.method).toEqual('PUT'); + + expect(errorServiceSpy.handle.calls.count()) + .toBe(1); + expect(errorServiceSpy.handle.calls.first().args) + .toEqual([ expectedErrorResponse.error ]); + }); + +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.service.ts new file mode 100644 index 0000000..7465577 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/app.service.ts @@ -0,0 +1,28 @@ +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable, catchError, throwError } from 'rxjs'; +import { environment } from '../environments/environment'; +import { ErrorService } from './error.service'; + +@Injectable({ + providedIn: 'root' +}) +export class AppService { + + constructor( + private http: HttpClient, + private errorService: ErrorService + ) { } + + change(): Observable { + return this.http.put(`${environment.apiUrl}/change`, {}) + .pipe( + catchError(this.errorHandler.bind(this)) + ); + } + + private errorHandler(error: HttpErrorResponse) { + this.errorService.handle(error.error) + return throwError(() => error) + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/auth.guard.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/auth.guard.spec.ts new file mode 100644 index 0000000..ab818c4 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/auth.guard.spec.ts @@ -0,0 +1,163 @@ +import { TestBed } from '@angular/core/testing'; +import { ActivatedRouteSnapshot, CanActivateFn, RouterStateSnapshot } from '@angular/router'; + +import { authGuard } from './auth.guard'; +import { AuthService } from './auth.service'; +import { SaveFutureRedirectService } from './save-future-redirect.service'; +import { Observable, of, throwError } from 'rxjs'; +import { WindowService } from './window.service'; +import { environment } from '../environments/environment'; + +describe('authGuard', () => { + const executeGuard: CanActivateFn = (...guardParameters) => + TestBed.runInInjectionContext(() => authGuard(...guardParameters)); + + let authService: jasmine.SpyObj; + let saveFutureRedirectService: jasmine.SpyObj; + let windowService: jasmine.SpyObj; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers:[ + { + provide: AuthService, useValue: jasmine.createSpyObj('AuthService', ['checkAuthenticate']) + }, + { + provide: SaveFutureRedirectService, useValue: jasmine.createSpyObj('SaveFutureRedirectService', ['redirectAndClearIfExist','saveIfNotExist']) + }, + { + provide: WindowService, useValue: jasmine.createSpyObj('WindowService', ['get']) + } + ] + }); + + + + authService = TestBed.inject(AuthService) as jasmine.SpyObj; + saveFutureRedirectService = TestBed.inject(SaveFutureRedirectService) as jasmine.SpyObj; + windowService = TestBed.inject(WindowService) as jasmine.SpyObj; + }); + + it('should be created', () => { + expect(executeGuard).toBeTruthy(); + }); + + it('should be true', done => { + // given (instead of when) + + authService.checkAuthenticate.and.returnValue(of(void 0)); + + // when + + (executeGuard({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) as Observable) + .subscribe({ + next: isSuccess => { + expect(isSuccess).toEqual(true); + done(); + } + }); + + // then (instead of verify) + + expect(authService.checkAuthenticate.calls.count()) + .toBe(1); + + expect(saveFutureRedirectService.redirectAndClearIfExist.calls.count()) + .toBe(1); + + + }); + + it('should be false', done => { + // given (instead of when) + + const error = { + "error":{ + "redirect_to": "/someroute" + }, + status: 401 + }; + + + const window = { + "location": { + "href": {} + } + } + + authService.checkAuthenticate.and.returnValue(new Observable(s => { + s.error(error); + s.complete(); + })); + + windowService.get.and.returnValue(window); + + // when + + (executeGuard({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) as Observable) + .subscribe({ + next: isSuccess => { + expect(window.location.href).toEqual(`${environment.apiUrl}${error.error.redirect_to}`); + expect(isSuccess).toEqual(false); + done(); + }, + error: error => done.fail('never call') + }); + + // then (instead of verify) + + expect(authService.checkAuthenticate.calls.count()) + .toBe(1); + + expect(saveFutureRedirectService.saveIfNotExist.calls.count()) + .toBe(1); + + + }); + + + it('should be false 2', done => { + // given (instead of when) + + const error = { + "error":{ + "redirect_to": "/someroute" + }, + status: 404 + }; + + + const window = { + "location": { + "href": {} + } + } + + authService.checkAuthenticate.and.returnValue(new Observable(s => { + s.error(error); + s.complete(); + })); + + windowService.get.and.returnValue(window); + + // when + + (executeGuard({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) as Observable) + .subscribe({ + next: isSuccess => { + expect(window.location.href).toEqual(`/error`); + expect(isSuccess).toEqual(false); + done(); + }, + error: error => done.fail('never call') + }); + + // then (instead of verify) + + expect(authService.checkAuthenticate.calls.count()) + .toBe(1); + + + }); + +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/auth.guard.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/auth.guard.ts new file mode 100644 index 0000000..7b4e9f4 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/auth.guard.ts @@ -0,0 +1,42 @@ +import { CanActivateFn } from '@angular/router'; +import { AuthService } from './auth.service'; +import { inject } from '@angular/core'; +import { SaveFutureRedirectService } from './save-future-redirect.service'; +import { catchError, first, map, of, tap } from 'rxjs'; +import { environment } from '../environments/environment'; +import { WindowService } from './window.service'; + +export const authGuard: CanActivateFn = (route, state) => { + + const authService = inject(AuthService); + const saveFutureRedirectService = inject(SaveFutureRedirectService); + const windowService = inject(WindowService); + + return authService.checkAuthenticate() + .pipe( + first(), + + map(() => { + + //получаем сохраненый url, удаляем и перередиректуемся + saveFutureRedirectService.redirectAndClearIfExist(); + + return true; + }), + catchError(error => { + + if (error?.error?.redirect_to && error?.status === 401) { + + //запоминаем в cookie текущий url + saveFutureRedirectService.saveIfNotExist(); + + // перенаправляем напрямую приложение + windowService.get().location.href = `${environment.apiUrl}${error.error.redirect_to}`; + } else { + windowService.get().location.href = '/error' + } + + return of(false); + }) + ); +}; \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/auth.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/auth.service.spec.ts new file mode 100644 index 0000000..8f86fbe --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/auth.service.spec.ts @@ -0,0 +1,111 @@ +import { TestBed } from '@angular/core/testing'; + +import { AuthService } from './auth.service'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { Observable, of } from 'rxjs'; +import { environment } from '../environments/environment'; +import { HttpErrorResponse } from '@angular/common/http'; + +describe('AuthService', () => { + let service: AuthService; + let httpTestingController: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ HttpClientTestingModule ] + }); + service = TestBed.inject(AuthService); + httpTestingController = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpTestingController.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('checkAuthenticate should be succeed', done => { + // given (instead of when) + + // when + + service.checkAuthenticate() + .subscribe({ + next: () => { + done() + }, + error: done.fail + }); + + const req = httpTestingController.expectOne(`${environment.apiUrl}/logged-in`); + + req.flush(null); + + // then (instead of verify) + + expect(req.request.method).toEqual('GET'); + + }); + + it('checkAuthenticate should be fail', done => { + // given (instead of when) + + const registrationHttpURL = `${environment.apiUrl}/logged-in`; + + const expectedErrorResponse = new HttpErrorResponse({ + error: '{"redirect_to":"/testtest"}', + url: registrationHttpURL, + status: 401, + statusText: 'Bad request' + }); + + + // when + + service.checkAuthenticate() + .subscribe({ + next: () => done.fail('data not expected'), + error: (actualErrorResponse: HttpErrorResponse) => { + expect(actualErrorResponse).toEqual(expectedErrorResponse); + done(); + } + }); + + const req = httpTestingController.expectOne(registrationHttpURL); + + req.flush(expectedErrorResponse.error, expectedErrorResponse); + + // then (instead of verify) + + expect(req.request.method).toEqual('GET'); + + }); + + it('logout should be succeed', done => { + // given (instead of when) + + // when + + service.logout() + .subscribe({ + next: () => { + done() + }, + error: done.fail + }); + + const req = httpTestingController.expectOne(`${environment.apiUrl}/logout`); + + req.flush(null); + + // then (instead of verify) + + expect(req.request.method).toEqual('POST'); + + }); + + +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/auth.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/auth.service.ts new file mode 100644 index 0000000..d3b66bb --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/auth.service.ts @@ -0,0 +1,20 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { environment } from '../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + + constructor(private http: HttpClient) {} + + checkAuthenticate(): Observable { + return this.http.get(`${environment.apiUrl}/logged-in`); + } + + logout(): Observable{ + return this.http.post(`${environment.apiUrl}/logout`, {}); + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/cookie.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/cookie.service.spec.ts new file mode 100644 index 0000000..9ba91e0 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/cookie.service.spec.ts @@ -0,0 +1,61 @@ +import { TestBed } from '@angular/core/testing'; + +import { CookieService } from './cookie.service'; + +describe('CookieService', () => { + let service: CookieService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + CookieService + ] + }); + service = TestBed.inject(CookieService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('setCookie, getCookie and deleteCookie', () => { + // given (instead of when) + + const expectedNotExistCookieValue: string | undefined = undefined; + + const expectedSomeCookie = { + name: 'myCookieName', + value: 'myCookieValue', + options: { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax', + expires: new Date((new Date()).getTime() + 1) + } + }; + + const expectedSomeCookie2 = { + name: 'myCookieName2', + value: 'myCookieValue2' + }; + + // when + + const actualNotExistCookieValue = service.getCookie('notExistCookie'); + + service.setCookie(expectedSomeCookie.name, expectedSomeCookie.value, expectedSomeCookie.options); + const actualSomeCookieValue = service.getCookie(expectedSomeCookie.name); + + service.setCookie(expectedSomeCookie2.name, expectedSomeCookie2.value); + service.deleteCookie(expectedSomeCookie2.name); + const actualSomeCookieValue2 = service.getCookie(expectedSomeCookie2.name); + + // then (instead of verify) + + expect(actualNotExistCookieValue).toEqual(expectedNotExistCookieValue); + expect(actualSomeCookieValue).toEqual(expectedSomeCookie.value); + expect(actualSomeCookieValue2).toEqual(expectedNotExistCookieValue); + + }) +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/cookie.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/cookie.service.ts new file mode 100644 index 0000000..edf0fc6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/cookie.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class CookieService { + + // возвращает куки с указанным name, + // или undefined, если ничего не найдено + getCookie(name: string): string | undefined { + + let matches = document.cookie.match(new RegExp( + "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)" + )); + return matches ? decodeURIComponent(matches[1]) : undefined; + } + + setCookie(name: string, value: string, options: any = {} ) { + + options = { + path: '/', + // при необходимости добавьте другие значения по умолчанию + ...options + }; + + if (options.expires instanceof Date) { + options.expires = options.expires.toUTCString(); + } + + let updatedCookie = encodeURIComponent(name) + "=" + encodeURIComponent(value); + + for (let optionKey in options) { + updatedCookie += "; " + optionKey; + let optionValue = options[optionKey]; + if (optionValue !== true) { + updatedCookie += "=" + optionValue; + } + } + + document.cookie = updatedCookie; + } + + deleteCookie(name: string) { + this.setCookie(name, "", { + 'max-age': -1 + }) + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error.service.spec.ts new file mode 100644 index 0000000..1716c7a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error.service.spec.ts @@ -0,0 +1,50 @@ +import { TestBed } from '@angular/core/testing'; + +import { ErrorService } from './error.service'; +import { MatSnackBar, MatSnackBarConfig} from '@angular/material/snack-bar'; + +describe('ErrorService', () => { + let service: ErrorService; + let matSnackBarSpy: jasmine.SpyObj; + + beforeEach(() => { + + TestBed.configureTestingModule({ + providers: [ + ErrorService, + { + provide: MatSnackBar, useValue: jasmine.createSpyObj('MatSnackBar', ['open']) + } + ] + }); + service = TestBed.inject(ErrorService); + matSnackBarSpy = TestBed.inject(MatSnackBar) as jasmine.SpyObj; + + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('handle', () => { + // given (instead of when) + + const expectedArgsOpen: [message: string, action?: string | undefined, config?: MatSnackBarConfig | undefined] = [ + 'Some Error', + service.actionCloseButtonName, + service.confOptionsSnackbar + ]; + + // when + + service.handle(expectedArgsOpen[0]); + + // then (instead of verify) + + expect(matSnackBarSpy.open.calls.count()) + .toBe(1); + expect(matSnackBarSpy.open.calls.first().args) + .toEqual(expectedArgsOpen); + }); + +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error.service.ts new file mode 100644 index 0000000..86d46c0 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error.service.ts @@ -0,0 +1,21 @@ +import {Injectable} from '@angular/core' +import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar' + +@Injectable({ + providedIn: 'root' +}) +export class ErrorService { + + readonly actionCloseButtonName = 'CLOSE'; + + readonly confOptionsSnackbar: MatSnackBarConfig = { + duration: 5000 + }; + + constructor(private matSnackBar: MatSnackBar){} + + handle(message: string) { + this.matSnackBar.open(message, this.actionCloseButtonName, this.confOptionsSnackbar); + } + +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error/error.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error/error.component.html new file mode 100644 index 0000000..d0528a6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error/error.component.html @@ -0,0 +1,16 @@ + + + logo +

Пришла ошибка от сервера аутентификации и авторизации

+ +

{{ errorOnPageModel.error }}

+ +

{{ errorOnPageModel.errorDescription }}

+ + + + cat_401 +
\ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error/error.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error/error.component.scss new file mode 100644 index 0000000..3b7d5c0 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error/error.component.scss @@ -0,0 +1,9 @@ +:host { + min-width: inherit; + width: inherit; + min-height: inherit; + height: inherit; + display: flex; + flex-direction: column; + align-items: center; +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error/error.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error/error.component.spec.ts new file mode 100644 index 0000000..14003ce --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error/error.component.spec.ts @@ -0,0 +1,161 @@ +import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing'; + +import { RouterTestingHarness } from '@angular/router/testing'; + +import { ErrorComponent } from './error.component'; +import { By } from '@angular/platform-browser'; +import { provideRouter } from '@angular/router'; +import { WindowService } from '../window.service'; +import { ErrorResponseCode } from '../models/error-response-code.model'; +import { ErrorOnPageModel } from '../models/error-on-page.model'; + +describe('ErrorComponent', () => { + + let harness: RouterTestingHarness; + + let error: string = '111'; + let error_description: string = '222'; + let reauthentication_location: string = '/qqq'; + + let windowServiceSpy: jasmine.SpyObj; + + beforeEach(async () => { + + await TestBed.configureTestingModule({ + imports: [ErrorComponent], + providers: [ + provideRouter([ + { + path: 'error', + component: ErrorComponent + } + ]), + { + provide: WindowService, useValue: jasmine.createSpyObj('WindowService', ['get']) + }, + ] + }) + .compileComponents() + .then(async () => { + harness = await RouterTestingHarness.create(); + + windowServiceSpy = TestBed.inject(WindowService) as jasmine.SpyObj; + + }); + }); + + it('should create', done => { + harness.navigateByUrl(`/error`, ErrorComponent) + .then(comp => { + expect(comp).toBeTruthy(); + + comp + .errorOnPageModel$ + .subscribe( + { + next: el => { + + expect(`Неизвестная ошибка`).toEqual(el.error); + expect("Неизвестное описание").toEqual(el.errorDescription); + expect('/').toEqual(el.reauthenticationLocation); + + done(); + }, + error: er => done.fail("errorOnPageModel is incorrect") + }); + }); + + }); + + it('should create with query param', done => { + + harness.navigateByUrl(`/error?error=${error}&error_description=${error_description}&reauthentication_location=${reauthentication_location}`, ErrorComponent) + .then(compWithQueryParam => { + //when + expect(compWithQueryParam).toBeTruthy(); + + compWithQueryParam + .errorOnPageModel$ + .subscribe( + { + next: el => { + + expect(`Ошибка (${error})`).toEqual(el.error); + expect(error_description).toEqual(el.errorDescription); + expect(reauthentication_location).toEqual(el.reauthenticationLocation); + + done(); + }, + error: er => done.fail("errorOnPageModel is incorrect") + } + ); + + }); + + }); + + it('getErrorResponseCodeRus', async () => { + + // given (instead of when) + await harness.navigateByUrl(`/error`, ErrorComponent); + harness.detectChanges(); + + let h2 = harness.routeDebugElement!.query(By.css("h2")).nativeElement as HTMLParagraphElement; + + // when + expect(h2.textContent).toEqual("Неизвестная ошибка"); + + + let changeAndCheckError = async (currentErrorResponseCode: ErrorResponseCode) => { + await harness.navigateByUrl(`/`); + await harness.navigateByUrl(`/error?error=${currentErrorResponseCode}`, ErrorComponent); + harness.detectChanges(); + h2 = harness.routeDebugElement!.query(By.css("h2")).nativeElement as HTMLParagraphElement; + expect(h2.textContent).toEqual(ErrorOnPageModel.getErrorResponseCodeRus(currentErrorResponseCode)); + } + + await changeAndCheckError(ErrorResponseCode.AccessDenied); + await changeAndCheckError(ErrorResponseCode.InsufficientScope); + await changeAndCheckError(ErrorResponseCode.InvalidClient); + await changeAndCheckError(ErrorResponseCode.InvalidGrant); + await changeAndCheckError(ErrorResponseCode.InvalidRedirectUri); + await changeAndCheckError(ErrorResponseCode.InvalidRequest); + await changeAndCheckError(ErrorResponseCode.InvalidScope); + await changeAndCheckError(ErrorResponseCode.InvalidToken); + await changeAndCheckError(ErrorResponseCode.ServerError); + await changeAndCheckError(ErrorResponseCode.TemporarilyUnavailable); + await changeAndCheckError(ErrorResponseCode.UnauthorizedClient); + await changeAndCheckError(ErrorResponseCode.UnsupportedGrantType); + await changeAndCheckError(ErrorResponseCode.UnsupportedResponseType); + await changeAndCheckError(ErrorResponseCode.UnsupportedTokenType); + + }); + + + it('goToAuth', fakeAsync(async () => { + + // given (instead of when) + + const window = { + "location": { + "href": {} + } + } + + const comp = await harness.navigateByUrl(`/error?error=${error}&error_description=${error_description}&reauthentication_location=${reauthentication_location}`, ErrorComponent); + harness.detectChanges(); + + windowServiceSpy.get.and.returnValue(window); + + // when + expect(comp).toBeTruthy(); + + harness.detectChanges(); + let button = harness.routeDebugElement!.query(By.css("button[mat-raised-button]")).nativeElement as HTMLButtonElement; + button.click(); + harness.detectChanges(); + + expect(window.location.href).toEqual(reauthentication_location); + })); + +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error/error.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error/error.component.ts new file mode 100644 index 0000000..f3cfcbf --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/error/error.component.ts @@ -0,0 +1,68 @@ +import { Component, OnDestroy } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Observable, Subject, first, map, takeUntil } from 'rxjs'; +import { ActivatedRoute } from '@angular/router'; + +import {MatButtonModule} from '@angular/material/button'; +import { WindowService } from '../window.service'; +import { ErrorOnPageModel } from '../models/error-on-page.model'; + +@Component({ + selector: 'app-error', + standalone: true, + imports: [CommonModule, MatButtonModule], + templateUrl: './error.component.html', + styleUrl: './error.component.scss' +}) +export class ErrorComponent implements OnDestroy { + // для очистки памяти rxjs + private readonly onDestroy = new Subject(); + + errorOnPageModel$: Observable; + + constructor( + private windowService: WindowService, + private route: ActivatedRoute + ) { + + this.errorOnPageModel$ = this.route.queryParamMap + .pipe( + first(), + map( + (paramMap) => { + let error: string | null = paramMap.get("error"); + let errorDescription: string | null = paramMap.get("error_description"); + let reauthenticationLocation: string | null = paramMap.get("reauthentication_location"); + + if (error) { + error = ErrorOnPageModel.getErrorResponseCodeRus(error); + } else { + error = "Неизвестная ошибка" + } + + if (!errorDescription) { + errorDescription = "Неизвестное описание" + } + + if (!reauthenticationLocation) { + reauthenticationLocation = '/'; + } + + return new ErrorOnPageModel(error, errorDescription, reauthenticationLocation); + } + ), + takeUntil(this.onDestroy) + ) + } + + goToAuth(url: string) { + this.windowService.get().location.href = url; + } + + ngOnDestroy(): void { + this.onDestroy.next(); + this.onDestroy.complete(); + } + + +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/generation-cookie-csrf.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/generation-cookie-csrf.service.spec.ts new file mode 100644 index 0000000..ecbff02 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/generation-cookie-csrf.service.spec.ts @@ -0,0 +1,51 @@ +import { TestBed } from '@angular/core/testing'; + +import { GenerationCookieCsrfService } from './generation-cookie-csrf.service'; +import { CookieService } from './cookie.service'; + +describe('GenerationCookieCsrfService', () => { + let service: GenerationCookieCsrfService; + let cookieServiceSpy: jasmine.SpyObj; + + beforeEach(() => { + + TestBed.configureTestingModule({ + providers: [ + GenerationCookieCsrfService, + { + provide: CookieService, useValue: jasmine.createSpyObj('CookieService', ['setCookie']) + } + ] + }); + service = TestBed.inject(GenerationCookieCsrfService); + cookieServiceSpy = TestBed.inject(CookieService) as jasmine.SpyObj; + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('generateCookieCsrf', () => { + // given (instead of when) + + const expectedCookieName = service.xsrfTokenCookieName; + + let actualCookieName: string = ''; + let actualCookieValue: string = ''; + cookieServiceSpy.setCookie.and.callFake( (name, value, options) => { + actualCookieName = name; + actualCookieValue = value; + }) + + // when + + service.generateCookieCsrf(); + + // then (instead of verify) + + expect(cookieServiceSpy.setCookie.calls.count()) + .toBe(1); + expect(actualCookieName).toEqual(expectedCookieName); + expect(actualCookieValue).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/generation-cookie-csrf.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/generation-cookie-csrf.service.ts new file mode 100644 index 0000000..eadfa65 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/generation-cookie-csrf.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@angular/core'; +import { CookieService } from './cookie.service'; + +@Injectable({ + providedIn: 'root' +}) +export class GenerationCookieCsrfService { + + + readonly xsrfTokenCookieName = "XSRF-TOKEN"; + + constructor( + private cookieService: CookieService + ) { } + + generateCookieCsrf(){ + + //устанавливаем cookie от CSRF + this.cookieService.setCookie(this.xsrfTokenCookieName, this.generateRandomString(20), { + path: '/', + 'max-age': 120, + secure: true, + samesite: 'lax' + }); + + } + + private generateRandomString(length: number): string { + + let result: string = ''; + let characters: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let charactersLength: number = characters.length; + + for (let i: number = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + + return result; + } + +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/layout/layout.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/layout/layout.component.html new file mode 100644 index 0000000..9c388b1 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/layout/layout.component.html @@ -0,0 +1,8 @@ + + APP WRITE AND READ + + + + \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/layout/layout.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/layout/layout.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/layout/layout.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/layout/layout.component.spec.ts new file mode 100644 index 0000000..c722465 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/layout/layout.component.spec.ts @@ -0,0 +1,80 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LayoutComponent } from './layout.component'; +import { GenerationCookieCsrfService } from '../generation-cookie-csrf.service'; +import { AuthService } from '../auth.service'; +import { SaveFutureRedirectService } from '../save-future-redirect.service'; +import { WindowService } from '../window.service'; +import { of } from 'rxjs'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; + +describe('LayoutComponent', () => { + let component: LayoutComponent; + let fixture: ComponentFixture; + + let generationCookieCsrfServiceSpy: jasmine.SpyObj; + let authServiceSpy: jasmine.SpyObj; + let saveFutureRedirectServiceSpy: jasmine.SpyObj; + let windowServiceSpy: jasmine.SpyObj; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LayoutComponent, HttpClientTestingModule], + providers: [ + { + provide: GenerationCookieCsrfService, useValue: jasmine.createSpyObj('GenerationCookieCsrfService', ['generateCookieCsrf']) + }, + { + provide: AuthService, useValue: jasmine.createSpyObj('AuthService', ['logout']) + }, + { + provide: SaveFutureRedirectService, useValue: jasmine.createSpyObj('SaveFutureRedirectService', ['saveIfNotExist']) + }, + { + provide: WindowService, useValue: jasmine.createSpyObj('WindowService', ['get']) + } + ] + }) + .compileComponents(); + + generationCookieCsrfServiceSpy = TestBed.inject(GenerationCookieCsrfService) as jasmine.SpyObj; + authServiceSpy = TestBed.inject(AuthService) as jasmine.SpyObj; + saveFutureRedirectServiceSpy = TestBed.inject(SaveFutureRedirectService) as jasmine.SpyObj; + windowServiceSpy = TestBed.inject(WindowService) as jasmine.SpyObj; + + fixture = TestBed.createComponent(LayoutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('logout', () => { + + const window = { + "location": { + "href": {} + } + } + + generationCookieCsrfServiceSpy.generateCookieCsrf.and.returnValue(); + + saveFutureRedirectServiceSpy.saveIfNotExist.and.returnValue(); + + windowServiceSpy.get.and.returnValue(window); + + authServiceSpy.logout.and.returnValue(of(void 0)); + + component.logout(); + + expect(generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count()) + .withContext('generationCookieCsrfServiceSpy.generateCookieCsrf.calls.count() == 1') + .toBe(1); + expect(saveFutureRedirectServiceSpy.saveIfNotExist.calls.count()) + .withContext('saveFutureRedirectServiceSpy.saveIfNotExist.calls.count() == 2') + .toBe(2); + + }); +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/layout/layout.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/layout/layout.component.ts new file mode 100644 index 0000000..cd6c539 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/layout/layout.component.ts @@ -0,0 +1,64 @@ +import { Component, OnDestroy } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Subject, takeUntil } from 'rxjs'; +import { environment } from '../../environments/environment'; +import { GenerationCookieCsrfService } from '../generation-cookie-csrf.service'; +import { AuthService } from '../auth.service'; +import { SaveFutureRedirectService } from '../save-future-redirect.service'; +import { RouterOutlet } from '@angular/router'; + +import {MatToolbarModule} from '@angular/material/toolbar'; +import {MatButtonModule} from '@angular/material/button'; +import { WindowService } from '../window.service'; + + +@Component({ + selector: 'app-layout', + standalone: true, + imports: [CommonModule, RouterOutlet, MatToolbarModule, MatButtonModule], + templateUrl: './layout.component.html', + styleUrl: './layout.component.scss' +}) +export class LayoutComponent implements OnDestroy { + + // для очистки памяти rxjs + private readonly onDestroy = new Subject(); + + constructor(private generationCookieCsrfService: GenerationCookieCsrfService, + private authService: AuthService, + private saveFutureRedirectService: SaveFutureRedirectService, + private windowService: WindowService + ) { + + } + + logout() { + + this.generationCookieCsrfService.generateCookieCsrf(); + + let logoutHandler: (() => void) = () => { + + //запоминаем в cookie текущий url + this.saveFutureRedirectService.saveIfNotExist(); + + this.windowService.get().location.href = `${environment.authServerBaseUrl}/oauth2/sessions/logout`; + }; + + this.authService.logout() + .pipe( + takeUntil(this.onDestroy) + ) + .subscribe({ + next: logoutHandler, + error: logoutHandler, + complete: logoutHandler + }); + + + } + + ngOnDestroy(): void { + this.onDestroy.next(); + this.onDestroy.complete(); + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/models/error-on-page.model.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/models/error-on-page.model.ts new file mode 100644 index 0000000..9e80220 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/models/error-on-page.model.ts @@ -0,0 +1,51 @@ +import { ErrorResponseCode } from "./error-response-code.model"; + +export class ErrorOnPageModel{ + error: string; + errorDescription: string; + reauthenticationLocation: string; + + constructor(error: string, errorDescription: string, reauthenticationLocation: string){ + this.error = error; + this.errorDescription = errorDescription; + this.reauthenticationLocation = reauthenticationLocation; + } + + + public static getErrorResponseCodeRus(errorResponseCodeStr: string): string { + let errorResponseCode: ErrorResponseCode = errorResponseCodeStr as ErrorResponseCode; + switch (errorResponseCode) { + case ErrorResponseCode.InvalidRequest: + return "Ошибка параметров запроса (invalid_request)"; + case ErrorResponseCode.UnauthorizedClient: + return "Клиент не может быть авторизован используя данный метод (unauthorized_client)"; + case ErrorResponseCode.AccessDenied: + return "Владелец ресурса или сервер авторизации отклонили запрос (access_denied)"; + case ErrorResponseCode.UnsupportedResponseType: + return "Сервер авторизации не поддерживает получение кода авторизации с помощью этого метода (unsupported_response_type)"; + case ErrorResponseCode.InvalidScope: + return "Запрошенные scopes недопустимы, неизвестны или имеют неправильную форму (invalid_scope)"; + case ErrorResponseCode.ServerError: + return "Сервер авторизации столкнулся с неожиданным условием, которое помешало ему выполнить запрос (server_error)"; + case ErrorResponseCode.TemporarilyUnavailable: + return "Сервер авторизации в настоящее время не может обработать запрос из-за временной перегрузки или технического обслуживания сервера (temporarily_unavailable)"; + case ErrorResponseCode.InsufficientScope: + return "Запрос требует более высоких привилегий, чем те, которые предоставляются токеном доступа (insufficient_scope)"; + case ErrorResponseCode.InvalidClient: + return "Ошибка проверки подлинности клиента (например, неизвестный клиент, проверка подлинности клиента не включена или неподдерживаемый метод проверки подлинности) (invalid_client)"; + case ErrorResponseCode.InvalidGrant: + return "Предоставленное разрешение на авторизацию (например, код авторизации, учетные данные владельца ресурса) или токен обновления недействительны, истек срок действия, отозваны, не соответствуют URI перенаправления, использованному в запросе на авторизацию, или были выданы другому клиенту. (invalid_grant)"; + case ErrorResponseCode.InvalidRedirectUri: + return "Значение одного или нескольких URI перенаправления недопустимо (invalid_redirect_uri)"; + case ErrorResponseCode.InvalidToken: + return "Предоставленный маркер доступа истек, отозван, имеет неправильную форму или недействителен по другим причинам (invalid_token)"; + case ErrorResponseCode.UnsupportedGrantType: + return "Тип предоставления авторизации не поддерживается сервером авторизации (unsupported_grant_type)"; + case ErrorResponseCode.UnsupportedTokenType: + return "Сервер авторизации не поддерживает отзыв представленного типа токена (unsupported_token_type)"; + default: + return `Ошибка (${errorResponseCodeStr})`; + } + } + +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/models/error-response-code.model.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/models/error-response-code.model.ts new file mode 100644 index 0000000..182aec1 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/models/error-response-code.model.ts @@ -0,0 +1,61 @@ +export const enum ErrorResponseCode { + + // invalid_request + // The request is missing a required parameter, includes an + // invalid parameter value, includes a parameter more than + // once, or is otherwise malformed. + InvalidRequest = "invalid_request", + + // unauthorized_client + // The client is not authorized to request an authorization + // code using this method. + UnauthorizedClient = "unauthorized_client", + + // access_denied + // The resource owner or authorization server denied the + // request. + AccessDenied = "access_denied", + + // unsupported_response_type + // The authorization server does not support obtaining an + // authorization code using this method. + UnsupportedResponseType = "unsupported_response_type", + + // invalid_scope + // The requested scope is invalid, unknown, or malformed. + InvalidScope = "invalid_scope", + + // server_error + // The authorization server encountered an unexpected + // condition that prevented it from fulfilling the request. + // (This error code is needed because a 500 Internal Server + // Error HTTP status code cannot be returned to the client + // via an HTTP redirect.) + ServerError = "server_error", + + // temporarily_unavailable + // The authorization server is currently unable to handle + // the request due to a temporary overloading or maintenance + // of the server. (This error code is needed because a 503 + // Service Unavailable HTTP status code cannot be returned + // to the client via an HTTP redirect.) + TemporarilyUnavailable = "temporarily_unavailable", + // insufficient_scope - + // The request requires higher privileges than provided by the access token. + InsufficientScope = "insufficient_scope", + // invalid_client - Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method) + InvalidClient = "invalid_client", + // invalid_grant - The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, + // revoked, does not match the redirection URI used in the authorization request, or was issued to another client. + InvalidGrant = "invalid_grant", + // invalid_redirect_uri - The value of one or more redirection URIs is invalid. + InvalidRedirectUri = "invalid_redirect_uri", + // invalid_token - The access token provided is expired, revoked, malformed, or invalid for other reasons + InvalidToken = "invalid_token", + //unsupported_grant_type - The authorization grant type is not supported by the authorization server. + UnsupportedGrantType = "unsupported_grant_type", + // unsupported_token_type - The authorization server does not support the revocation of the presented token type + UnsupportedTokenType = "unsupported_token_type" + + + } \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/not-found/not-found.component.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/not-found/not-found.component.html new file mode 100644 index 0000000..b24386f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/not-found/not-found.component.html @@ -0,0 +1,11 @@ +
+

404

+
Page not found
+ cry +
\ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/not-found/not-found.component.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/not-found/not-found.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/not-found/not-found.component.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/not-found/not-found.component.spec.ts new file mode 100644 index 0000000..1111bad --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/not-found/not-found.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NotFoundComponent } from './not-found.component'; + +describe('NotFoundComponent', () => { + let component: NotFoundComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [NotFoundComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(NotFoundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/not-found/not-found.component.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/not-found/not-found.component.ts new file mode 100644 index 0000000..504afca --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/not-found/not-found.component.ts @@ -0,0 +1,13 @@ +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@Component({ + selector: 'app-not-found', + standalone: true, + imports: [CommonModule], + templateUrl: './not-found.component.html', + styleUrl: './not-found.component.scss' +}) +export class NotFoundComponent { + +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/save-future-redirect.service.spec.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/save-future-redirect.service.spec.ts new file mode 100644 index 0000000..9983dea --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/save-future-redirect.service.spec.ts @@ -0,0 +1,86 @@ +import { TestBed } from '@angular/core/testing'; + +import { SaveFutureRedirectService } from './save-future-redirect.service'; +import { CookieService } from './cookie.service'; +import { Router, provideRouter } from '@angular/router'; +import { routes } from './app.routes'; + +describe('SaveFutureRedirectService', () => { + let service: SaveFutureRedirectService; + let cookieServiceSpy: jasmine.SpyObj; + let router: jasmine.SpyObj; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + { + provide: CookieService, useValue: jasmine.createSpyObj('CookieService', ['setCookie', 'getCookie', 'deleteCookie']) + }, + { + provide: Router, useValue: jasmine.createSpyObj('Router', ['navigateByUrl']) + }, + ] + }); + service = TestBed.inject(SaveFutureRedirectService); + cookieServiceSpy = TestBed.inject(CookieService) as jasmine.SpyObj; + + router = TestBed.inject(Router) as jasmine.SpyObj; + + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('saveIfNotExist', () => { + // given (instead of when) + + const expectedCookieName = service.urlCookieName; + + let actualCookieName: string = 'n'; + let actualCookieValue: string = 'v'; + + cookieServiceSpy.getCookie.and.returnValue(undefined); + + cookieServiceSpy.setCookie.and.callFake( (name, value, options) => { + actualCookieName = name; + actualCookieValue = value; + }) + + // when + + service.saveIfNotExist(); + + // then (instead of verify) + + expect(cookieServiceSpy.getCookie.calls.count()) + .toBe(1); + expect(cookieServiceSpy.setCookie.calls.count()) + .toBe(1); + expect(actualCookieName).toEqual(expectedCookieName); + + }); + + it('redirectAndClearIfExist', () => { + // given (instead of when) + + const url: string = 'some_url'; + + cookieServiceSpy.getCookie.and.returnValue(url); + + // when + + service.redirectAndClearIfExist(); + + // then (instead of verify) + + expect(cookieServiceSpy.getCookie.calls.count()) + .toBe(1); + expect(cookieServiceSpy.deleteCookie.calls.count()) + .toBe(1); + + expect(router.navigateByUrl.calls.count()) + .toBe(1); + + }); +}); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/save-future-redirect.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/save-future-redirect.service.ts new file mode 100644 index 0000000..00b691a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/save-future-redirect.service.ts @@ -0,0 +1,38 @@ +import { Injectable } from '@angular/core'; +import { CookieService } from './cookie.service'; +import { Router } from '@angular/router'; + +@Injectable({ + providedIn: 'root' +}) +export class SaveFutureRedirectService { + + readonly urlCookieName = "CURRENT_URI"; + + constructor( + private cookieService: CookieService, + private router: Router + ) { } + + saveIfNotExist(){ + + if(!this.cookieService.getCookie(this.urlCookieName)){ + this.cookieService.setCookie(this.urlCookieName, this.router.url, { + path: '/', + secure: true, + samesite: 'lax' + }); + } + } + + redirectAndClearIfExist(){ + let url:string | undefined = this.cookieService.getCookie(this.urlCookieName); + + if(url){ + + this.cookieService.deleteCookie(this.urlCookieName); + + this.router.navigateByUrl(url); + } + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/window.service.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/window.service.ts new file mode 100644 index 0000000..7719b0d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/app/window.service.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class WindowService { + + get(): any{ + return window; + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/assets/.gitkeep b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/assets/cat_401.png b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/assets/cat_401.png new file mode 100644 index 0000000..08c7ff3 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/assets/cat_401.png differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/assets/error.jpg b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/assets/error.jpg new file mode 100644 index 0000000..5c162cd Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/assets/error.jpg differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/assets/logo.png b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/assets/logo.png new file mode 100644 index 0000000..0ef3fde Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/assets/logo.png differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/environments/environment.development.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/environments/environment.development.ts new file mode 100644 index 0000000..d66eb62 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/environments/environment.development.ts @@ -0,0 +1,5 @@ +export const environment = { + production: false, + apiUrl: 'http://localhost:3000', + authServerBaseUrl: '' +}; \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/environments/environment.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/environments/environment.ts new file mode 100644 index 0000000..28d6ae7 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/environments/environment.ts @@ -0,0 +1,5 @@ +export const environment = { + production: true, + apiUrl: '/api', + authServerBaseUrl: 'https://authorization-server.com' + }; \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/favicon.ico b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/favicon.ico new file mode 100644 index 0000000..ee799c9 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/favicon.ico differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz0dL_nz.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz0dL_nz.woff2 new file mode 100644 index 0000000..b52eb7a Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz0dL_nz.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz4dL_nz.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz4dL_nz.woff2 new file mode 100644 index 0000000..b038adc Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz4dL_nz.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2 new file mode 100644 index 0000000..327bebc Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzQdL_nz.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzQdL_nz.woff2 new file mode 100644 index 0000000..d63fc4a Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzQdL_nz.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc-CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc-CsTKlA.woff2 new file mode 100644 index 0000000..44e685b Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc-CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc0CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc0CsTKlA.woff2 new file mode 100644 index 0000000..a852d38 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc0CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc3CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc3CsTKlA.woff2 new file mode 100644 index 0000000..7eb322e Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc3CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc6CsQ.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc6CsQ.woff2 new file mode 100644 index 0000000..0ff2f81 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc6CsQ.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc-CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc-CsTKlA.woff2 new file mode 100644 index 0000000..7de8183 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc-CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.woff2 new file mode 100644 index 0000000..e2c6e1b Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc3CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc3CsTKlA.woff2 new file mode 100644 index 0000000..ccda5b9 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc3CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc6CsQ.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc6CsQ.woff2 new file mode 100644 index 0000000..7d846b1 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc6CsQ.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 new file mode 100644 index 0000000..7f69a29 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2 new file mode 100644 index 0000000..23e4e3c Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 new file mode 100644 index 0000000..d930de9 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2 new file mode 100644 index 0000000..b6653fb Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2 new file mode 100644 index 0000000..33a8438 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 new file mode 100644 index 0000000..0177669 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2 new file mode 100644 index 0000000..bb9e774 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 new file mode 100644 index 0000000..fe58be2 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2 new file mode 100644 index 0000000..4d9bb7d Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2 new file mode 100644 index 0000000..e9e3430 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxIIzI.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxIIzI.woff2 new file mode 100644 index 0000000..a5cc283 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxIIzI.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2 new file mode 100644 index 0000000..1758b7c Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2 new file mode 100644 index 0000000..d1d8a40 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 new file mode 100644 index 0000000..1862df5 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xIIzI.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xIIzI.woff2 new file mode 100644 index 0000000..2741d4f Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xIIzI.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 new file mode 100644 index 0000000..0171146 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2 new file mode 100644 index 0000000..1db8422 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 new file mode 100644 index 0000000..6362d7f Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2 new file mode 100644 index 0000000..b4b5df7 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 new file mode 100644 index 0000000..530e22c Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2 new file mode 100644 index 0000000..507809d Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 new file mode 100644 index 0000000..ef8c883 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2 new file mode 100644 index 0000000..8323edd Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 new file mode 100644 index 0000000..7d3dc27 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 new file mode 100644 index 0000000..6679743 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 new file mode 100644 index 0000000..32b25ee Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 new file mode 100644 index 0000000..7445d92 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2 new file mode 100644 index 0000000..5d7fed5 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2 new file mode 100644 index 0000000..4e8fac6 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfBBc4.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfBBc4.woff2 new file mode 100644 index 0000000..802499d Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfBBc4.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2 new file mode 100644 index 0000000..8f77a78 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2 new file mode 100644 index 0000000..9ddedcd Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu4mxK.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu4mxK.woff2 new file mode 100644 index 0000000..1a53701 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu4mxK.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 new file mode 100644 index 0000000..de226b3 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2 new file mode 100644 index 0000000..e3eabe7 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 new file mode 100644 index 0000000..de83f9c Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/Roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-brands-400.ttf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-brands-400.ttf new file mode 100644 index 0000000..774d51a Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-brands-400.ttf differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-brands-400.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-brands-400.woff2 new file mode 100644 index 0000000..71e3185 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-brands-400.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-regular-400.ttf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-regular-400.ttf new file mode 100644 index 0000000..8a9d634 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-regular-400.ttf differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-regular-400.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-regular-400.woff2 new file mode 100644 index 0000000..7f02168 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-regular-400.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-solid-900.ttf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-solid-900.ttf new file mode 100644 index 0000000..993dbe1 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-solid-900.ttf differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-solid-900.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-solid-900.woff2 new file mode 100644 index 0000000..5c16cd3 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-solid-900.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-v4compatibility.ttf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-v4compatibility.ttf new file mode 100644 index 0000000..ab6ae22 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-v4compatibility.ttf differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-v4compatibility.woff2 b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-v4compatibility.woff2 new file mode 100644 index 0000000..9027e38 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/fonts/fontawesome-free/fa-v4compatibility.woff2 differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/index.html b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/index.html new file mode 100644 index 0000000..58456bb --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/index.html @@ -0,0 +1,13 @@ + + + + + Horns and hooves (write and read client) + + + + + + + + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/main.server.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/main.server.ts new file mode 100644 index 0000000..4b9d4d1 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/main.server.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { config } from './app/app.config.server'; + +const bootstrap = () => bootstrapApplication(AppComponent, config); + +export default bootstrap; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/main.ts b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/main.ts new file mode 100644 index 0000000..35b00f3 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/main.ts @@ -0,0 +1,6 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { AppComponent } from './app/app.component'; + +bootstrapApplication(AppComponent, appConfig) + .catch((err) => console.error(err)); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/_shame.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/_shame.scss new file mode 100644 index 0000000..e69de29 diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/base/_typography.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/base/_typography.scss new file mode 100644 index 0000000..0ed46af --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/base/_typography.scss @@ -0,0 +1,434 @@ + + +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'), url(/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz0dL_nz.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'), url(/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzQdL_nz.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'), url(/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEz4dL_nz.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'), url(/fonts/Roboto/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: local('Roboto Light Italic'), local('Roboto-LightItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: local('Roboto Light Italic'), local('Roboto-LightItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: local('Roboto Light Italic'), local('Roboto-LightItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: local('Roboto Light Italic'), local('Roboto-LightItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: local('Roboto Italic'), local('Roboto-Italic'), url(/fonts/Roboto/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: local('Roboto Italic'), local('Roboto-Italic'), url(/fonts/Roboto/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: local('Roboto Italic'), local('Roboto-Italic'), url(/fonts/Roboto/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: local('Roboto Italic'), local('Roboto-Italic'), url(/fonts/Roboto/KFOkCnqEu92Fr1Mu51xIIzI.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc-CsTKlA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51S7ACc6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: local('Roboto Black Italic'), local('Roboto-BlackItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: local('Roboto Black Italic'), local('Roboto-BlackItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc-CsTKlA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: local('Roboto Black Italic'), local('Roboto-BlackItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: local('Roboto Black Italic'), local('Roboto-BlackItalic'), url(/fonts/Roboto/KFOjCnqEu92Fr1Mu51TLBCc6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin'), local('Roboto-Thin'), url(/fonts/Roboto/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin'), local('Roboto-Thin'), url(/fonts/Roboto/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin'), local('Roboto-Thin'), url(/fonts/Roboto/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: local('Roboto Thin'), local('Roboto-Thin'), url(/fonts/Roboto/KFOkCnqEu92Fr1MmgVxIIzI.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: local('Roboto Light'), local('Roboto-Light'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: local('Roboto Light'), local('Roboto-Light'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: local('Roboto Light'), local('Roboto-Light'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: local('Roboto Light'), local('Roboto-Light'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmSU5fBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Roboto'), local('Roboto-Regular'), url(/fonts/Roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Roboto'), local('Roboto-Regular'), url(/fonts/Roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Roboto'), local('Roboto-Regular'), url(/fonts/Roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Roboto'), local('Roboto-Regular'), url(/fonts/Roboto/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium'), local('Roboto-Medium'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium'), local('Roboto-Medium'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium'), local('Roboto-Medium'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: local('Roboto Medium'), local('Roboto-Medium'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold'), local('Roboto-Bold'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold'), local('Roboto-Bold'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold'), local('Roboto-Bold'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local('Roboto Bold'), local('Roboto-Bold'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmWUlfBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: local('Roboto Black'), local('Roboto-Black'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: local('Roboto Black'), local('Roboto-Black'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: local('Roboto Black'), local('Roboto-Black'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: local('Roboto Black'), local('Roboto-Black'), url(/fonts/Roboto/KFOlCnqEu92Fr1MmYUtfBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/_material_io.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/_material_io.scss new file mode 100644 index 0000000..2c88a9a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/_material_io.scss @@ -0,0 +1,35 @@ +// Custom Theming for Angular Material +// For more information: https://material.angular.io/guide/theming +@use '@angular/material' as mat; +// Plus imports for other components in your app. + +// Include the common styles for Angular Material. We include this here so that you only +// have to load a single css file for Angular Material in your app. +// Be sure that you only ever include this mixin once! +@include mat.core(); + +// Define the palettes for your theme using the Material Design palettes available in palette.scss +// (imported above). For each palette, you can optionally specify a default, lighter, and darker +// hue. Available color palettes: https://material.io/design/color/ +$authorization-frontend-primary: mat.define-palette(mat.$indigo-palette); +$authorization-frontend-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); + +// The warn palette is optional (defaults to red). +$authorization-frontend-warn: mat.define-palette(mat.$red-palette); + +// Create the theme object. A theme consists of configurations for individual +// theming systems such as "color" or "typography". +$authorization-frontend-theme: mat.define-light-theme(( + color: ( + primary: $authorization-frontend-primary, + accent: $authorization-frontend-accent, + warn: $authorization-frontend-warn, + ) +)); + +// Include theme styles for core and each component used in your app. +// Alternatively, you can import and @include the theme mixins for each component +// that you are using. +@include mat.all-component-themes($authorization-frontend-theme); + +/* You can add global styles to this file, and also import other style files */ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_animated.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_animated.scss new file mode 100644 index 0000000..93555b2 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_animated.scss @@ -0,0 +1,153 @@ +// animating icons +// -------------------------- + +.#{$fa-css-prefix}-beat { + animation-name: #{$fa-css-prefix}-beat; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, ease-in-out); +} + +.#{$fa-css-prefix}-bounce { + animation-name: #{$fa-css-prefix}-bounce; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(0.280, 0.840, 0.420, 1)); +} + +.#{$fa-css-prefix}-fade { + animation-name: #{$fa-css-prefix}-fade; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(.4,0,.6,1)); +} + +.#{$fa-css-prefix}-beat-fade { + animation-name: #{$fa-css-prefix}-beat-fade; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(.4,0,.6,1)); +} + +.#{$fa-css-prefix}-flip { + animation-name: #{$fa-css-prefix}-flip; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, ease-in-out); +} + +.#{$fa-css-prefix}-shake { + animation-name: #{$fa-css-prefix}-shake; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, linear); +} + +.#{$fa-css-prefix}-spin { + animation-name: #{$fa-css-prefix}-spin; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 2s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, linear); +} + +.#{$fa-css-prefix}-spin-reverse { + --#{$fa-css-prefix}-animation-direction: reverse; +} + +.#{$fa-css-prefix}-pulse, +.#{$fa-css-prefix}-spin-pulse { + animation-name: #{$fa-css-prefix}-spin; + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, steps(8)); +} + +// if agent or operating system prefers reduced motion, disable animations +// see: https://www.smashingmagazine.com/2020/09/design-reduced-motion-sensitivities/ +// see: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion +@media (prefers-reduced-motion: reduce) { + .#{$fa-css-prefix}-beat, + .#{$fa-css-prefix}-bounce, + .#{$fa-css-prefix}-fade, + .#{$fa-css-prefix}-beat-fade, + .#{$fa-css-prefix}-flip, + .#{$fa-css-prefix}-pulse, + .#{$fa-css-prefix}-shake, + .#{$fa-css-prefix}-spin, + .#{$fa-css-prefix}-spin-pulse { + animation-delay: -1ms; + animation-duration: 1ms; + animation-iteration-count: 1; + transition-delay: 0s; + transition-duration: 0s; + } +} + +@keyframes #{$fa-css-prefix}-beat { + 0%, 90% { transform: scale(1); } + 45% { transform: scale(var(--#{$fa-css-prefix}-beat-scale, 1.25)); } +} + +@keyframes #{$fa-css-prefix}-bounce { + 0% { transform: scale(1,1) translateY(0); } + 10% { transform: scale(var(--#{$fa-css-prefix}-bounce-start-scale-x, 1.1),var(--#{$fa-css-prefix}-bounce-start-scale-y, 0.9)) translateY(0); } + 30% { transform: scale(var(--#{$fa-css-prefix}-bounce-jump-scale-x, 0.9),var(--#{$fa-css-prefix}-bounce-jump-scale-y, 1.1)) translateY(var(--#{$fa-css-prefix}-bounce-height, -0.5em)); } + 50% { transform: scale(var(--#{$fa-css-prefix}-bounce-land-scale-x, 1.05),var(--#{$fa-css-prefix}-bounce-land-scale-y, 0.95)) translateY(0); } + 57% { transform: scale(1,1) translateY(var(--#{$fa-css-prefix}-bounce-rebound, -0.125em)); } + 64% { transform: scale(1,1) translateY(0); } + 100% { transform: scale(1,1) translateY(0); } +} + +@keyframes #{$fa-css-prefix}-fade { + 50% { opacity: var(--#{$fa-css-prefix}-fade-opacity, 0.4); } +} + +@keyframes #{$fa-css-prefix}-beat-fade { + 0%, 100% { + opacity: var(--#{$fa-css-prefix}-beat-fade-opacity, 0.4); + transform: scale(1); + } + 50% { + opacity: 1; + transform: scale(var(--#{$fa-css-prefix}-beat-fade-scale, 1.125)); + } +} + +@keyframes #{$fa-css-prefix}-flip { + 50% { + transform: rotate3d(var(--#{$fa-css-prefix}-flip-x, 0), var(--#{$fa-css-prefix}-flip-y, 1), var(--#{$fa-css-prefix}-flip-z, 0), var(--#{$fa-css-prefix}-flip-angle, -180deg)); + } +} + +@keyframes #{$fa-css-prefix}-shake { + 0% { transform: rotate(-15deg); } + 4% { transform: rotate(15deg); } + 8%, 24% { transform: rotate(-18deg); } + 12%, 28% { transform: rotate(18deg); } + 16% { transform: rotate(-22deg); } + 20% { transform: rotate(22deg); } + 32% { transform: rotate(-12deg); } + 36% { transform: rotate(12deg); } + 40%, 100% { transform: rotate(0deg); } +} + +@keyframes #{$fa-css-prefix}-spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_bordered-pulled.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_bordered-pulled.scss new file mode 100644 index 0000000..9068253 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_bordered-pulled.scss @@ -0,0 +1,20 @@ +// bordered + pulled icons +// ------------------------- + +.#{$fa-css-prefix}-border { + border-color: var(--#{$fa-css-prefix}-border-color, #{$fa-border-color}); + border-radius: var(--#{$fa-css-prefix}-border-radius, #{$fa-border-radius}); + border-style: var(--#{$fa-css-prefix}-border-style, #{$fa-border-style}); + border-width: var(--#{$fa-css-prefix}-border-width, #{$fa-border-width}); + padding: var(--#{$fa-css-prefix}-border-padding, #{$fa-border-padding}); +} + +.#{$fa-css-prefix}-pull-left { + float: left; + margin-right: var(--#{$fa-css-prefix}-pull-margin, #{$fa-pull-margin}); +} + +.#{$fa-css-prefix}-pull-right { + float: right; + margin-left: var(--#{$fa-css-prefix}-pull-margin, #{$fa-pull-margin}); +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_core.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_core.scss new file mode 100644 index 0000000..1b2fd99 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_core.scss @@ -0,0 +1,43 @@ +// base icon class definition +// ------------------------- + +.#{$fa-css-prefix} { + font-family: var(--#{$fa-css-prefix}-style-family, '#{$fa-style-family}'); + font-weight: var(--#{$fa-css-prefix}-style, #{$fa-style}); +} + +.#{$fa-css-prefix}, +.#{$fa-css-prefix}-classic, +.#{$fa-css-prefix}-sharp, +.fas, +.#{$fa-css-prefix}-solid, +.far, +.#{$fa-css-prefix}-regular, +.fab, +.#{$fa-css-prefix}-brands { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: var(--#{$fa-css-prefix}-display, #{$fa-display}); + font-style: normal; + font-variant: normal; + line-height: 1; + text-rendering: auto; +} + +.fas, +.#{$fa-css-prefix}-classic, +.#{$fa-css-prefix}-solid, +.far, +.#{$fa-css-prefix}-regular { + font-family: 'Font Awesome 6 Free'; +} + +.fab, +.#{$fa-css-prefix}-brands { + font-family: 'Font Awesome 6 Brands'; +} + + +%fa-icon { + @include fa-icon; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_fixed-width.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_fixed-width.scss new file mode 100644 index 0000000..7234236 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_fixed-width.scss @@ -0,0 +1,7 @@ +// fixed-width icons +// ------------------------- + +.#{$fa-css-prefix}-fw { + text-align: center; + width: $fa-fw-width; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_functions.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_functions.scss new file mode 100644 index 0000000..a17ffe8 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_functions.scss @@ -0,0 +1,57 @@ +// functions +// -------------------------- + +// fa-content: convenience function used to set content property +@function fa-content($fa-var) { + @return unquote("\"#{ $fa-var }\""); +} + +// fa-divide: Originally obtained from the Bootstrap https://github.com/twbs/bootstrap +// +// Licensed under: The MIT License (MIT) +// +// Copyright (c) 2011-2021 Twitter, Inc. +// Copyright (c) 2011-2021 The Bootstrap Authors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +@function fa-divide($dividend, $divisor, $precision: 10) { + $sign: if($dividend > 0 and $divisor > 0, 1, -1); + $dividend: abs($dividend); + $divisor: abs($divisor); + $quotient: 0; + $remainder: $dividend; + @if $dividend == 0 { + @return 0; + } + @if $divisor == 0 { + @error "Cannot divide by 0"; + } + @if $divisor == 1 { + @return $dividend; + } + @while $remainder >= $divisor { + $quotient: $quotient + 1; + $remainder: $remainder - $divisor; + } + @if $remainder > 0 and $precision > 0 { + $remainder: fa-divide($remainder * 10, $divisor, $precision - 1) * .1; + } + @return ($quotient + $remainder) * $sign; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_icons.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_icons.scss new file mode 100644 index 0000000..0f55926 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_icons.scss @@ -0,0 +1,10 @@ +// specific icon class definition +// ------------------------- + +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen +readers do not read off random characters that represent icons */ + +@each $name, $icon in $fa-icons { + .#{$fa-css-prefix}-#{$name}::before { content: unquote("\"#{ $icon }\""); } +} + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_list.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_list.scss new file mode 100644 index 0000000..ced36e2 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_list.scss @@ -0,0 +1,18 @@ +// icons in a list +// ------------------------- + +.#{$fa-css-prefix}-ul { + list-style-type: none; + margin-left: var(--#{$fa-css-prefix}-li-margin, #{$fa-li-margin}); + padding-left: 0; + + > li { position: relative; } +} + +.#{$fa-css-prefix}-li { + left: calc(var(--#{$fa-css-prefix}-li-width, #{$fa-li-width}) * -1); + position: absolute; + text-align: center; + width: var(--#{$fa-css-prefix}-li-width, #{$fa-li-width}); + line-height: inherit; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_mixins.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_mixins.scss new file mode 100644 index 0000000..e06b69a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_mixins.scss @@ -0,0 +1,75 @@ +// mixins +// -------------------------- + +// base rendering for an icon +@mixin fa-icon { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + display: inline-block; + font-style: normal; + font-variant: normal; + font-weight: normal; + line-height: 1; +} + +// sets relative font-sizing and alignment (in _sizing) +@mixin fa-size ($font-size) { + font-size: fa-divide($font-size, $fa-size-scale-base) * 1em; // converts step in sizing scale into an em-based value that's relative to the scale's base + line-height: fa-divide(1, $font-size) * 1em; // sets the line-height of the icon back to that of it's parent + vertical-align: (fa-divide(6, $font-size) - fa-divide(3, 8)) * 1em; // vertically centers the icon taking into account the surrounding text's descender +} + +// only display content to screen readers +// see: https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/ +// see: https://hugogiraudel.com/2016/10/13/css-hide-and-seek/ +@mixin fa-sr-only() { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +// use in conjunction with .sr-only to only display content when it's focused +@mixin fa-sr-only-focusable() { + &:not(:focus) { + @include fa-sr-only(); + } +} + +// sets a specific icon family to use alongside style + icon mixins + +// convenience mixins for declaring pseudo-elements by CSS variable, +// including all style-specific font properties, and both the ::before +// and ::after elements in the duotone case. +@mixin fa-icon-solid($fa-var) { + @extend %fa-icon; + @extend .fa-solid; + + &::before { + content: unquote("\"#{ $fa-var }\""); + } +} + +@mixin fa-icon-regular($fa-var) { + @extend %fa-icon; + @extend .fa-regular; + + &::before { + content: unquote("\"#{ $fa-var }\""); + } +} + +@mixin fa-icon-brands($fa-var) { + @extend %fa-icon; + @extend .fa-brands; + + &::before { + content: unquote("\"#{ $fa-var }\""); + } +} + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_rotated-flipped.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_rotated-flipped.scss new file mode 100644 index 0000000..f27fabe --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_rotated-flipped.scss @@ -0,0 +1,31 @@ +// rotating + flipping icons +// ------------------------- + +.#{$fa-css-prefix}-rotate-90 { + transform: rotate(90deg); +} + +.#{$fa-css-prefix}-rotate-180 { + transform: rotate(180deg); +} + +.#{$fa-css-prefix}-rotate-270 { + transform: rotate(270deg); +} + +.#{$fa-css-prefix}-flip-horizontal { + transform: scale(-1, 1); +} + +.#{$fa-css-prefix}-flip-vertical { + transform: scale(1, -1); +} + +.#{$fa-css-prefix}-flip-both, +.#{$fa-css-prefix}-flip-horizontal.#{$fa-css-prefix}-flip-vertical { + transform: scale(-1, -1); +} + +.#{$fa-css-prefix}-rotate-by { + transform: rotate(var(--#{$fa-css-prefix}-rotate-angle, none)); +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_screen-reader.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_screen-reader.scss new file mode 100644 index 0000000..2beb887 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_screen-reader.scss @@ -0,0 +1,14 @@ +// screen-reader utilities +// ------------------------- + +// only display content to screen readers +.sr-only, +.#{$fa-css-prefix}-sr-only { + @include fa-sr-only; +} + +// use in conjunction with .sr-only to only display content when it's focused +.sr-only-focusable, +.#{$fa-css-prefix}-sr-only-focusable { + @include fa-sr-only-focusable; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_shims.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_shims.scss new file mode 100644 index 0000000..7809aa6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_shims.scss @@ -0,0 +1,2042 @@ +.#{$fa-css-prefix}.#{$fa-css-prefix}-glass:before { content: unquote("\"#{ $fa-var-martini-glass-empty }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-o:before { content: unquote("\"#{ $fa-var-envelope }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-o:before { content: unquote("\"#{ $fa-var-star }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-remove:before { content: unquote("\"#{ $fa-var-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-close:before { content: unquote("\"#{ $fa-var-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gear:before { content: unquote("\"#{ $fa-var-gear }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-trash-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-trash-o:before { content: unquote("\"#{ $fa-var-trash-can }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-home:before { content: unquote("\"#{ $fa-var-house }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-o:before { content: unquote("\"#{ $fa-var-file }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-clock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-clock-o:before { content: unquote("\"#{ $fa-var-clock }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-down:before { content: unquote("\"#{ $fa-var-circle-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-up:before { content: unquote("\"#{ $fa-var-circle-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-play-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-play-circle-o:before { content: unquote("\"#{ $fa-var-circle-play }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-repeat:before { content: unquote("\"#{ $fa-var-arrow-rotate-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rotate-right:before { content: unquote("\"#{ $fa-var-arrow-rotate-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-refresh:before { content: unquote("\"#{ $fa-var-arrows-rotate }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-list-alt { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-list-alt:before { content: unquote("\"#{ $fa-var-rectangle-list }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dedent:before { content: unquote("\"#{ $fa-var-outdent }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-video-camera:before { content: unquote("\"#{ $fa-var-video }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-picture-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-picture-o:before { content: unquote("\"#{ $fa-var-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-photo { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-photo:before { content: unquote("\"#{ $fa-var-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-image { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-image:before { content: unquote("\"#{ $fa-var-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-map-marker:before { content: unquote("\"#{ $fa-var-location-dot }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pencil-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pencil-square-o:before { content: unquote("\"#{ $fa-var-pen-to-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-edit { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-edit:before { content: unquote("\"#{ $fa-var-pen-to-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-share-square-o:before { content: unquote("\"#{ $fa-var-share-from-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-square-o:before { content: unquote("\"#{ $fa-var-square-check }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows:before { content: unquote("\"#{ $fa-var-up-down-left-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-circle-o:before { content: unquote("\"#{ $fa-var-circle-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-circle-o:before { content: unquote("\"#{ $fa-var-circle-check }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mail-forward:before { content: unquote("\"#{ $fa-var-share }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-expand:before { content: unquote("\"#{ $fa-var-up-right-and-down-left-from-center }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-compress:before { content: unquote("\"#{ $fa-var-down-left-and-up-right-to-center }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eye { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eye-slash { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-warning:before { content: unquote("\"#{ $fa-var-triangle-exclamation }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar:before { content: unquote("\"#{ $fa-var-calendar-days }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows-v:before { content: unquote("\"#{ $fa-var-up-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows-h:before { content: unquote("\"#{ $fa-var-left-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bar-chart:before { content: unquote("\"#{ $fa-var-chart-column }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bar-chart-o:before { content: unquote("\"#{ $fa-var-chart-column }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitter-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitter-square:before { content: unquote("\"#{ $fa-var-square-twitter }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-square:before { content: unquote("\"#{ $fa-var-square-facebook }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gears:before { content: unquote("\"#{ $fa-var-gears }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-up:before { content: unquote("\"#{ $fa-var-thumbs-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-down:before { content: unquote("\"#{ $fa-var-thumbs-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-heart-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-heart-o:before { content: unquote("\"#{ $fa-var-heart }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sign-out:before { content: unquote("\"#{ $fa-var-right-from-bracket }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin-square:before { content: unquote("\"#{ $fa-var-linkedin }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumb-tack:before { content: unquote("\"#{ $fa-var-thumbtack }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-external-link:before { content: unquote("\"#{ $fa-var-up-right-from-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sign-in:before { content: unquote("\"#{ $fa-var-right-to-bracket }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-github-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-github-square:before { content: unquote("\"#{ $fa-var-square-github }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-lemon-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-lemon-o:before { content: unquote("\"#{ $fa-var-lemon }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-square-o:before { content: unquote("\"#{ $fa-var-square }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bookmark-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bookmark-o:before { content: unquote("\"#{ $fa-var-bookmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitter { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook:before { content: unquote("\"#{ $fa-var-facebook-f }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-f { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-f:before { content: unquote("\"#{ $fa-var-facebook-f }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-github { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-credit-card { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-feed:before { content: unquote("\"#{ $fa-var-rss }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hdd-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hdd-o:before { content: unquote("\"#{ $fa-var-hard-drive }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-right:before { content: unquote("\"#{ $fa-var-hand-point-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-left:before { content: unquote("\"#{ $fa-var-hand-point-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-up:before { content: unquote("\"#{ $fa-var-hand-point-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-down:before { content: unquote("\"#{ $fa-var-hand-point-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-globe:before { content: unquote("\"#{ $fa-var-earth-americas }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tasks:before { content: unquote("\"#{ $fa-var-bars-progress }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows-alt:before { content: unquote("\"#{ $fa-var-maximize }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-group:before { content: unquote("\"#{ $fa-var-users }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-chain:before { content: unquote("\"#{ $fa-var-link }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cut:before { content: unquote("\"#{ $fa-var-scissors }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-files-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-files-o:before { content: unquote("\"#{ $fa-var-copy }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-floppy-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-floppy-o:before { content: unquote("\"#{ $fa-var-floppy-disk }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-save { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-save:before { content: unquote("\"#{ $fa-var-floppy-disk }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-navicon:before { content: unquote("\"#{ $fa-var-bars }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-reorder:before { content: unquote("\"#{ $fa-var-bars }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-magic:before { content: unquote("\"#{ $fa-var-wand-magic-sparkles }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest-square:before { content: unquote("\"#{ $fa-var-square-pinterest }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-square:before { content: unquote("\"#{ $fa-var-square-google-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus:before { content: unquote("\"#{ $fa-var-google-plus-g }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-money:before { content: unquote("\"#{ $fa-var-money-bill-1 }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-unsorted:before { content: unquote("\"#{ $fa-var-sort }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-desc:before { content: unquote("\"#{ $fa-var-sort-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-asc:before { content: unquote("\"#{ $fa-var-sort-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin:before { content: unquote("\"#{ $fa-var-linkedin-in }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rotate-left:before { content: unquote("\"#{ $fa-var-arrow-rotate-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-legal:before { content: unquote("\"#{ $fa-var-gavel }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tachometer:before { content: unquote("\"#{ $fa-var-gauge-high }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dashboard:before { content: unquote("\"#{ $fa-var-gauge-high }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-comment-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-comment-o:before { content: unquote("\"#{ $fa-var-comment }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-comments-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-comments-o:before { content: unquote("\"#{ $fa-var-comments }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-flash:before { content: unquote("\"#{ $fa-var-bolt }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-clipboard:before { content: unquote("\"#{ $fa-var-paste }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-lightbulb-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-lightbulb-o:before { content: unquote("\"#{ $fa-var-lightbulb }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-exchange:before { content: unquote("\"#{ $fa-var-right-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cloud-download:before { content: unquote("\"#{ $fa-var-cloud-arrow-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cloud-upload:before { content: unquote("\"#{ $fa-var-cloud-arrow-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-o:before { content: unquote("\"#{ $fa-var-bell }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cutlery:before { content: unquote("\"#{ $fa-var-utensils }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-text-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-text-o:before { content: unquote("\"#{ $fa-var-file-lines }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-building-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-building-o:before { content: unquote("\"#{ $fa-var-building }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hospital-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hospital-o:before { content: unquote("\"#{ $fa-var-hospital }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tablet:before { content: unquote("\"#{ $fa-var-tablet-screen-button }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mobile:before { content: unquote("\"#{ $fa-var-mobile-screen-button }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mobile-phone:before { content: unquote("\"#{ $fa-var-mobile-screen-button }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-o:before { content: unquote("\"#{ $fa-var-circle }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mail-reply:before { content: unquote("\"#{ $fa-var-reply }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-github-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-o:before { content: unquote("\"#{ $fa-var-folder }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-open-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-open-o:before { content: unquote("\"#{ $fa-var-folder-open }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-smile-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-smile-o:before { content: unquote("\"#{ $fa-var-face-smile }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-frown-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-frown-o:before { content: unquote("\"#{ $fa-var-face-frown }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-meh-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-meh-o:before { content: unquote("\"#{ $fa-var-face-meh }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-keyboard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-keyboard-o:before { content: unquote("\"#{ $fa-var-keyboard }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-flag-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-flag-o:before { content: unquote("\"#{ $fa-var-flag }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mail-reply-all:before { content: unquote("\"#{ $fa-var-reply-all }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-o:before { content: unquote("\"#{ $fa-var-star-half-stroke }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-empty { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-empty:before { content: unquote("\"#{ $fa-var-star-half-stroke }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-full { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-full:before { content: unquote("\"#{ $fa-var-star-half-stroke }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-code-fork:before { content: unquote("\"#{ $fa-var-code-branch }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-chain-broken:before { content: unquote("\"#{ $fa-var-link-slash }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-unlink:before { content: unquote("\"#{ $fa-var-link-slash }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-o:before { content: unquote("\"#{ $fa-var-calendar }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-maxcdn { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-html5 { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-css3 { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-unlock-alt:before { content: unquote("\"#{ $fa-var-unlock }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-minus-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-minus-square-o:before { content: unquote("\"#{ $fa-var-square-minus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-level-up:before { content: unquote("\"#{ $fa-var-turn-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-level-down:before { content: unquote("\"#{ $fa-var-turn-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pencil-square:before { content: unquote("\"#{ $fa-var-square-pen }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-external-link-square:before { content: unquote("\"#{ $fa-var-square-up-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-compass { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-down:before { content: unquote("\"#{ $fa-var-square-caret-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-down:before { content: unquote("\"#{ $fa-var-square-caret-down }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-up:before { content: unquote("\"#{ $fa-var-square-caret-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-up:before { content: unquote("\"#{ $fa-var-square-caret-up }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-right:before { content: unquote("\"#{ $fa-var-square-caret-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-right:before { content: unquote("\"#{ $fa-var-square-caret-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eur:before { content: unquote("\"#{ $fa-var-euro-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-euro:before { content: unquote("\"#{ $fa-var-euro-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gbp:before { content: unquote("\"#{ $fa-var-sterling-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-usd:before { content: unquote("\"#{ $fa-var-dollar-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dollar:before { content: unquote("\"#{ $fa-var-dollar-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-inr:before { content: unquote("\"#{ $fa-var-indian-rupee-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rupee:before { content: unquote("\"#{ $fa-var-indian-rupee-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-jpy:before { content: unquote("\"#{ $fa-var-yen-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cny:before { content: unquote("\"#{ $fa-var-yen-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rmb:before { content: unquote("\"#{ $fa-var-yen-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yen:before { content: unquote("\"#{ $fa-var-yen-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rub:before { content: unquote("\"#{ $fa-var-ruble-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ruble:before { content: unquote("\"#{ $fa-var-ruble-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rouble:before { content: unquote("\"#{ $fa-var-ruble-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-krw:before { content: unquote("\"#{ $fa-var-won-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-won:before { content: unquote("\"#{ $fa-var-won-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-btc { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitcoin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitcoin:before { content: unquote("\"#{ $fa-var-btc }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-text:before { content: unquote("\"#{ $fa-var-file-lines }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-alpha-asc:before { content: unquote("\"#{ $fa-var-arrow-down-a-z }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-alpha-desc:before { content: unquote("\"#{ $fa-var-arrow-down-z-a }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-amount-asc:before { content: unquote("\"#{ $fa-var-arrow-down-short-wide }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-amount-desc:before { content: unquote("\"#{ $fa-var-arrow-down-wide-short }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-numeric-asc:before { content: unquote("\"#{ $fa-var-arrow-down-1-9 }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-numeric-desc:before { content: unquote("\"#{ $fa-var-arrow-down-9-1 }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-square:before { content: unquote("\"#{ $fa-var-square-youtube }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-xing { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-xing-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-xing-square:before { content: unquote("\"#{ $fa-var-square-xing }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-play { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-play:before { content: unquote("\"#{ $fa-var-youtube }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dropbox { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stack-overflow { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-instagram { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-flickr { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-adn { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitbucket { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitbucket-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitbucket-square:before { content: unquote("\"#{ $fa-var-bitbucket }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tumblr { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tumblr-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-tumblr-square:before { content: unquote("\"#{ $fa-var-square-tumblr }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-down:before { content: unquote("\"#{ $fa-var-down-long }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-up:before { content: unquote("\"#{ $fa-var-up-long }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-left:before { content: unquote("\"#{ $fa-var-left-long }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-right:before { content: unquote("\"#{ $fa-var-right-long }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-apple { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-windows { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-android { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-linux { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dribbble { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-skype { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-foursquare { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-trello { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gratipay { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gittip { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-gittip:before { content: unquote("\"#{ $fa-var-gratipay }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sun-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-sun-o:before { content: unquote("\"#{ $fa-var-sun }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-moon-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-moon-o:before { content: unquote("\"#{ $fa-var-moon }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vk { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-weibo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-renren { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pagelines { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stack-exchange { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-right:before { content: unquote("\"#{ $fa-var-circle-right }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-left:before { content: unquote("\"#{ $fa-var-circle-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-left:before { content: unquote("\"#{ $fa-var-square-caret-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-left:before { content: unquote("\"#{ $fa-var-square-caret-left }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dot-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-dot-circle-o:before { content: unquote("\"#{ $fa-var-circle-dot }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo-square:before { content: unquote("\"#{ $fa-var-square-vimeo }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-try:before { content: unquote("\"#{ $fa-var-turkish-lira-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-turkish-lira:before { content: unquote("\"#{ $fa-var-turkish-lira-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-plus-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-plus-square-o:before { content: unquote("\"#{ $fa-var-square-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-slack { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wordpress { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-openid { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-institution:before { content: unquote("\"#{ $fa-var-building-columns }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bank:before { content: unquote("\"#{ $fa-var-building-columns }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mortar-board:before { content: unquote("\"#{ $fa-var-graduation-cap }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yahoo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit-square:before { content: unquote("\"#{ $fa-var-square-reddit }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stumbleupon-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stumbleupon { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-delicious { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-digg { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pied-piper-pp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pied-piper-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-drupal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-joomla { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-behance { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-behance-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-behance-square:before { content: unquote("\"#{ $fa-var-square-behance }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-steam { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-steam-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-steam-square:before { content: unquote("\"#{ $fa-var-square-steam }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-automobile:before { content: unquote("\"#{ $fa-var-car }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cab:before { content: unquote("\"#{ $fa-var-taxi }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-spotify { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-deviantart { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-soundcloud { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-pdf-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-pdf-o:before { content: unquote("\"#{ $fa-var-file-pdf }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-word-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-word-o:before { content: unquote("\"#{ $fa-var-file-word }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-excel-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-excel-o:before { content: unquote("\"#{ $fa-var-file-excel }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-powerpoint-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-powerpoint-o:before { content: unquote("\"#{ $fa-var-file-powerpoint }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-image-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-image-o:before { content: unquote("\"#{ $fa-var-file-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-photo-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-photo-o:before { content: unquote("\"#{ $fa-var-file-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-picture-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-picture-o:before { content: unquote("\"#{ $fa-var-file-image }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-archive-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-archive-o:before { content: unquote("\"#{ $fa-var-file-zipper }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-zip-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-zip-o:before { content: unquote("\"#{ $fa-var-file-zipper }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-audio-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-audio-o:before { content: unquote("\"#{ $fa-var-file-audio }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-sound-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-sound-o:before { content: unquote("\"#{ $fa-var-file-audio }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-video-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-video-o:before { content: unquote("\"#{ $fa-var-file-video }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-movie-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-movie-o:before { content: unquote("\"#{ $fa-var-file-video }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-code-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-code-o:before { content: unquote("\"#{ $fa-var-file-code }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vine { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-codepen { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-jsfiddle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-life-bouy:before { content: unquote("\"#{ $fa-var-life-ring }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-life-buoy:before { content: unquote("\"#{ $fa-var-life-ring }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-life-saver:before { content: unquote("\"#{ $fa-var-life-ring }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-support:before { content: unquote("\"#{ $fa-var-life-ring }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-o-notch:before { content: unquote("\"#{ $fa-var-circle-notch }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-rebel { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ra { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-ra:before { content: unquote("\"#{ $fa-var-rebel }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-resistance { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-resistance:before { content: unquote("\"#{ $fa-var-rebel }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-empire { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ge { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-ge:before { content: unquote("\"#{ $fa-var-empire }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-git-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-git-square:before { content: unquote("\"#{ $fa-var-square-git }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-git { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hacker-news { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-y-combinator-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-y-combinator-square:before { content: unquote("\"#{ $fa-var-hacker-news }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc-square:before { content: unquote("\"#{ $fa-var-hacker-news }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-tencent-weibo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-qq { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-weixin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wechat { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wechat:before { content: unquote("\"#{ $fa-var-weixin }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-send:before { content: unquote("\"#{ $fa-var-paper-plane }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-paper-plane-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-paper-plane-o:before { content: unquote("\"#{ $fa-var-paper-plane }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-send-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-send-o:before { content: unquote("\"#{ $fa-var-paper-plane }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-thin { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-thin:before { content: unquote("\"#{ $fa-var-circle }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-header:before { content: unquote("\"#{ $fa-var-heading }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-futbol-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-futbol-o:before { content: unquote("\"#{ $fa-var-futbol }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-soccer-ball-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-soccer-ball-o:before { content: unquote("\"#{ $fa-var-futbol }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-slideshare { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitch { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yelp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-newspaper-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-newspaper-o:before { content: unquote("\"#{ $fa-var-newspaper }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-paypal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-wallet { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-visa { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-mastercard { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-discover { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-amex { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-paypal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-stripe { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-slash-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-slash-o:before { content: unquote("\"#{ $fa-var-bell-slash }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-trash:before { content: unquote("\"#{ $fa-var-trash-can }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-copyright { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eyedropper:before { content: unquote("\"#{ $fa-var-eye-dropper }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-area-chart:before { content: unquote("\"#{ $fa-var-chart-area }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pie-chart:before { content: unquote("\"#{ $fa-var-chart-pie }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-line-chart:before { content: unquote("\"#{ $fa-var-chart-line }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-lastfm { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-lastfm-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-lastfm-square:before { content: unquote("\"#{ $fa-var-square-lastfm }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ioxhost { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-angellist { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc:before { content: unquote("\"#{ $fa-var-closed-captioning }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ils:before { content: unquote("\"#{ $fa-var-shekel-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-shekel:before { content: unquote("\"#{ $fa-var-shekel-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sheqel:before { content: unquote("\"#{ $fa-var-shekel-sign }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-buysellads { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-connectdevelop { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-dashcube { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-forumbee { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-leanpub { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sellsy { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-shirtsinbulk { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-simplybuilt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-skyatlas { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-diamond { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-diamond:before { content: unquote("\"#{ $fa-var-gem }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-transgender:before { content: unquote("\"#{ $fa-var-mars-and-venus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-intersex:before { content: unquote("\"#{ $fa-var-mars-and-venus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-transgender-alt:before { content: unquote("\"#{ $fa-var-transgender }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-official { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-official:before { content: unquote("\"#{ $fa-var-facebook }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest-p { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-whatsapp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hotel:before { content: unquote("\"#{ $fa-var-bed }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-viacoin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-medium { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-y-combinator { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc:before { content: unquote("\"#{ $fa-var-y-combinator }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-optin-monster { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-opencart { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-expeditedssl { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-4:before { content: unquote("\"#{ $fa-var-battery-full }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery:before { content: unquote("\"#{ $fa-var-battery-full }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-3:before { content: unquote("\"#{ $fa-var-battery-three-quarters }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-2:before { content: unquote("\"#{ $fa-var-battery-half }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-1:before { content: unquote("\"#{ $fa-var-battery-quarter }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-0:before { content: unquote("\"#{ $fa-var-battery-empty }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-object-group { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-object-ungroup { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-sticky-note-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-sticky-note-o:before { content: unquote("\"#{ $fa-var-note-sticky }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-jcb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-diners-club { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-clone { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-o:before { content: unquote("\"#{ $fa-var-hourglass }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-1:before { content: unquote("\"#{ $fa-var-hourglass-start }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-2:before { content: unquote("\"#{ $fa-var-hourglass-half }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-3:before { content: unquote("\"#{ $fa-var-hourglass-end }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-rock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-rock-o:before { content: unquote("\"#{ $fa-var-hand-back-fist }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-grab-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-grab-o:before { content: unquote("\"#{ $fa-var-hand-back-fist }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-paper-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-paper-o:before { content: unquote("\"#{ $fa-var-hand }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-stop-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-stop-o:before { content: unquote("\"#{ $fa-var-hand }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-scissors-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-scissors-o:before { content: unquote("\"#{ $fa-var-hand-scissors }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-lizard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-lizard-o:before { content: unquote("\"#{ $fa-var-hand-lizard }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-spock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-spock-o:before { content: unquote("\"#{ $fa-var-hand-spock }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-pointer-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-pointer-o:before { content: unquote("\"#{ $fa-var-hand-pointer }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-peace-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-peace-o:before { content: unquote("\"#{ $fa-var-hand-peace }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-registered { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-creative-commons { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gg { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gg-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-odnoklassniki { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-odnoklassniki-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-odnoklassniki-square:before { content: unquote("\"#{ $fa-var-square-odnoklassniki }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-get-pocket { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wikipedia-w { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-safari { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-chrome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-firefox { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-opera { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-internet-explorer { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-television:before { content: unquote("\"#{ $fa-var-tv }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-contao { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-500px { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-amazon { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-plus-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-plus-o:before { content: unquote("\"#{ $fa-var-calendar-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-minus-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-minus-o:before { content: unquote("\"#{ $fa-var-calendar-minus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-times-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-times-o:before { content: unquote("\"#{ $fa-var-calendar-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-check-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-check-o:before { content: unquote("\"#{ $fa-var-calendar-check }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-map-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-map-o:before { content: unquote("\"#{ $fa-var-map }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-commenting:before { content: unquote("\"#{ $fa-var-comment-dots }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-commenting-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-commenting-o:before { content: unquote("\"#{ $fa-var-comment-dots }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-houzz { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo:before { content: unquote("\"#{ $fa-var-vimeo-v }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-black-tie { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-fonticons { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit-alien { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-edge { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-credit-card-alt:before { content: unquote("\"#{ $fa-var-credit-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-codiepie { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-modx { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-fort-awesome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-usb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-product-hunt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-mixcloud { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-scribd { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pause-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pause-circle-o:before { content: unquote("\"#{ $fa-var-circle-pause }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-stop-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-stop-circle-o:before { content: unquote("\"#{ $fa-var-circle-stop }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bluetooth { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bluetooth-b { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-gitlab { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wpbeginner { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wpforms { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-envira { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wheelchair-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wheelchair-alt:before { content: unquote("\"#{ $fa-var-accessible-icon }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-question-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-question-circle-o:before { content: unquote("\"#{ $fa-var-circle-question }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-volume-control-phone:before { content: unquote("\"#{ $fa-var-phone-volume }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-asl-interpreting:before { content: unquote("\"#{ $fa-var-hands-asl-interpreting }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-deafness:before { content: unquote("\"#{ $fa-var-ear-deaf }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-hard-of-hearing:before { content: unquote("\"#{ $fa-var-ear-deaf }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-glide { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-glide-g { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-signing:before { content: unquote("\"#{ $fa-var-hands }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-viadeo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-viadeo-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-viadeo-square:before { content: unquote("\"#{ $fa-var-square-viadeo }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-ghost { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-ghost:before { content: unquote("\"#{ $fa-var-snapchat }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-square:before { content: unquote("\"#{ $fa-var-square-snapchat }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-pied-piper { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-first-order { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-yoast { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-themeisle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-official { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-official:before { content: unquote("\"#{ $fa-var-google-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-circle:before { content: unquote("\"#{ $fa-var-google-plus }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-font-awesome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-fa { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-fa:before { content: unquote("\"#{ $fa-var-font-awesome }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-handshake-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-handshake-o:before { content: unquote("\"#{ $fa-var-handshake }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-open-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-open-o:before { content: unquote("\"#{ $fa-var-envelope-open }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-linode { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-book-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-book-o:before { content: unquote("\"#{ $fa-var-address-book }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vcard:before { content: unquote("\"#{ $fa-var-address-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-card-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-card-o:before { content: unquote("\"#{ $fa-var-address-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-vcard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-vcard-o:before { content: unquote("\"#{ $fa-var-address-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-circle-o:before { content: unquote("\"#{ $fa-var-circle-user }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-o:before { content: unquote("\"#{ $fa-var-user }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-id-badge { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-drivers-license:before { content: unquote("\"#{ $fa-var-id-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-id-card-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-id-card-o:before { content: unquote("\"#{ $fa-var-id-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-drivers-license-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-drivers-license-o:before { content: unquote("\"#{ $fa-var-id-card }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-quora { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-free-code-camp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-telegram { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-4:before { content: unquote("\"#{ $fa-var-temperature-full }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer:before { content: unquote("\"#{ $fa-var-temperature-full }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-3:before { content: unquote("\"#{ $fa-var-temperature-three-quarters }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-2:before { content: unquote("\"#{ $fa-var-temperature-half }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-1:before { content: unquote("\"#{ $fa-var-temperature-quarter }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-0:before { content: unquote("\"#{ $fa-var-temperature-empty }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bathtub:before { content: unquote("\"#{ $fa-var-bath }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-s15:before { content: unquote("\"#{ $fa-var-bath }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-maximize { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-restore { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-rectangle:before { content: unquote("\"#{ $fa-var-rectangle-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-close-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-close-o:before { content: unquote("\"#{ $fa-var-rectangle-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-rectangle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-rectangle-o:before { content: unquote("\"#{ $fa-var-rectangle-xmark }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-bandcamp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-grav { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-etsy { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-imdb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-ravelry { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-eercast { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-eercast:before { content: unquote("\"#{ $fa-var-sellcast }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-snowflake-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-snowflake-o:before { content: unquote("\"#{ $fa-var-snowflake }\""); } + +.#{$fa-css-prefix}.#{$fa-css-prefix}-superpowers { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-wpexplorer { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + +.#{$fa-css-prefix}.#{$fa-css-prefix}-meetup { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} + diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_sizing.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_sizing.scss new file mode 100644 index 0000000..e171e7d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_sizing.scss @@ -0,0 +1,16 @@ +// sizing icons +// ------------------------- + +// literal magnification scale +@for $i from 1 through 10 { + .#{$fa-css-prefix}-#{$i}x { + font-size: $i * 1em; + } +} + +// step-based scale (with alignment) +@each $size, $value in $fa-sizes { + .#{$fa-css-prefix}-#{$size} { + @include fa-size($value); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_stacked.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_stacked.scss new file mode 100644 index 0000000..d9a9d4e --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_stacked.scss @@ -0,0 +1,32 @@ +// stacking icons +// ------------------------- + +.#{$fa-css-prefix}-stack { + display: inline-block; + height: 2em; + line-height: 2em; + position: relative; + vertical-align: $fa-stack-vertical-align; + width: $fa-stack-width; +} + +.#{$fa-css-prefix}-stack-1x, +.#{$fa-css-prefix}-stack-2x { + left: 0; + position: absolute; + text-align: center; + width: 100%; + z-index: var(--#{$fa-css-prefix}-stack-z-index, #{$fa-stack-z-index}); +} + +.#{$fa-css-prefix}-stack-1x { + line-height: inherit; +} + +.#{$fa-css-prefix}-stack-2x { + font-size: 2em; +} + +.#{$fa-css-prefix}-inverse { + color: var(--#{$fa-css-prefix}-inverse, #{$fa-inverse}); +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_variables.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_variables.scss new file mode 100644 index 0000000..858541f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/_variables.scss @@ -0,0 +1,4961 @@ +// variables +// -------------------------- + +$fa-css-prefix : fa !default; +$fa-style : 900 !default; +$fa-style-family : "Font Awesome 6 Free" !default; + +$fa-display : inline-block !default; + +$fa-fw-width : fa-divide(20em, 16) !default; +$fa-inverse : #fff !default; + +$fa-border-color : #eee !default; +$fa-border-padding : .2em .25em .15em !default; +$fa-border-radius : .1em !default; +$fa-border-style : solid !default; +$fa-border-width : .08em !default; + +$fa-size-scale-2xs : 10 !default; +$fa-size-scale-xs : 12 !default; +$fa-size-scale-sm : 14 !default; +$fa-size-scale-base : 16 !default; +$fa-size-scale-lg : 20 !default; +$fa-size-scale-xl : 24 !default; +$fa-size-scale-2xl : 32 !default; + +$fa-sizes: ( + "2xs" : $fa-size-scale-2xs, + "xs" : $fa-size-scale-xs, + "sm" : $fa-size-scale-sm, + "lg" : $fa-size-scale-lg, + "xl" : $fa-size-scale-xl, + "2xl" : $fa-size-scale-2xl +) !default; + +$fa-li-width : 2em !default; +$fa-li-margin : $fa-li-width * fa-divide(5, 4) !default; + +$fa-pull-margin : .3em !default; + +$fa-primary-opacity : 1 !default; +$fa-secondary-opacity : .4 !default; + +$fa-stack-vertical-align: middle !default; +$fa-stack-width : ($fa-fw-width * 2) !default; +$fa-stack-z-index : auto !default; + +$fa-font-display : block !default; +$fa-font-path : "/fonts/fontawesome-free" !default; + +$fa-var-0: \30; +$fa-var-1: \31; +$fa-var-2: \32; +$fa-var-3: \33; +$fa-var-4: \34; +$fa-var-5: \35; +$fa-var-6: \36; +$fa-var-7: \37; +$fa-var-8: \38; +$fa-var-9: \39; +$fa-var-fill-drip: \f576; +$fa-var-arrows-to-circle: \e4bd; +$fa-var-circle-chevron-right: \f138; +$fa-var-chevron-circle-right: \f138; +$fa-var-at: \40; +$fa-var-trash-can: \f2ed; +$fa-var-trash-alt: \f2ed; +$fa-var-text-height: \f034; +$fa-var-user-xmark: \f235; +$fa-var-user-times: \f235; +$fa-var-stethoscope: \f0f1; +$fa-var-message: \f27a; +$fa-var-comment-alt: \f27a; +$fa-var-info: \f129; +$fa-var-down-left-and-up-right-to-center: \f422; +$fa-var-compress-alt: \f422; +$fa-var-explosion: \e4e9; +$fa-var-file-lines: \f15c; +$fa-var-file-alt: \f15c; +$fa-var-file-text: \f15c; +$fa-var-wave-square: \f83e; +$fa-var-ring: \f70b; +$fa-var-building-un: \e4d9; +$fa-var-dice-three: \f527; +$fa-var-calendar-days: \f073; +$fa-var-calendar-alt: \f073; +$fa-var-anchor-circle-check: \e4aa; +$fa-var-building-circle-arrow-right: \e4d1; +$fa-var-volleyball: \f45f; +$fa-var-volleyball-ball: \f45f; +$fa-var-arrows-up-to-line: \e4c2; +$fa-var-sort-down: \f0dd; +$fa-var-sort-desc: \f0dd; +$fa-var-circle-minus: \f056; +$fa-var-minus-circle: \f056; +$fa-var-door-open: \f52b; +$fa-var-right-from-bracket: \f2f5; +$fa-var-sign-out-alt: \f2f5; +$fa-var-atom: \f5d2; +$fa-var-soap: \e06e; +$fa-var-icons: \f86d; +$fa-var-heart-music-camera-bolt: \f86d; +$fa-var-microphone-lines-slash: \f539; +$fa-var-microphone-alt-slash: \f539; +$fa-var-bridge-circle-check: \e4c9; +$fa-var-pump-medical: \e06a; +$fa-var-fingerprint: \f577; +$fa-var-hand-point-right: \f0a4; +$fa-var-magnifying-glass-location: \f689; +$fa-var-search-location: \f689; +$fa-var-forward-step: \f051; +$fa-var-step-forward: \f051; +$fa-var-face-smile-beam: \f5b8; +$fa-var-smile-beam: \f5b8; +$fa-var-flag-checkered: \f11e; +$fa-var-football: \f44e; +$fa-var-football-ball: \f44e; +$fa-var-school-circle-exclamation: \e56c; +$fa-var-crop: \f125; +$fa-var-angles-down: \f103; +$fa-var-angle-double-down: \f103; +$fa-var-users-rectangle: \e594; +$fa-var-people-roof: \e537; +$fa-var-people-line: \e534; +$fa-var-beer-mug-empty: \f0fc; +$fa-var-beer: \f0fc; +$fa-var-diagram-predecessor: \e477; +$fa-var-arrow-up-long: \f176; +$fa-var-long-arrow-up: \f176; +$fa-var-fire-flame-simple: \f46a; +$fa-var-burn: \f46a; +$fa-var-person: \f183; +$fa-var-male: \f183; +$fa-var-laptop: \f109; +$fa-var-file-csv: \f6dd; +$fa-var-menorah: \f676; +$fa-var-truck-plane: \e58f; +$fa-var-record-vinyl: \f8d9; +$fa-var-face-grin-stars: \f587; +$fa-var-grin-stars: \f587; +$fa-var-bong: \f55c; +$fa-var-spaghetti-monster-flying: \f67b; +$fa-var-pastafarianism: \f67b; +$fa-var-arrow-down-up-across-line: \e4af; +$fa-var-spoon: \f2e5; +$fa-var-utensil-spoon: \f2e5; +$fa-var-jar-wheat: \e517; +$fa-var-envelopes-bulk: \f674; +$fa-var-mail-bulk: \f674; +$fa-var-file-circle-exclamation: \e4eb; +$fa-var-circle-h: \f47e; +$fa-var-hospital-symbol: \f47e; +$fa-var-pager: \f815; +$fa-var-address-book: \f2b9; +$fa-var-contact-book: \f2b9; +$fa-var-strikethrough: \f0cc; +$fa-var-k: \4b; +$fa-var-landmark-flag: \e51c; +$fa-var-pencil: \f303; +$fa-var-pencil-alt: \f303; +$fa-var-backward: \f04a; +$fa-var-caret-right: \f0da; +$fa-var-comments: \f086; +$fa-var-paste: \f0ea; +$fa-var-file-clipboard: \f0ea; +$fa-var-code-pull-request: \e13c; +$fa-var-clipboard-list: \f46d; +$fa-var-truck-ramp-box: \f4de; +$fa-var-truck-loading: \f4de; +$fa-var-user-check: \f4fc; +$fa-var-vial-virus: \e597; +$fa-var-sheet-plastic: \e571; +$fa-var-blog: \f781; +$fa-var-user-ninja: \f504; +$fa-var-person-arrow-up-from-line: \e539; +$fa-var-scroll-torah: \f6a0; +$fa-var-torah: \f6a0; +$fa-var-broom-ball: \f458; +$fa-var-quidditch: \f458; +$fa-var-quidditch-broom-ball: \f458; +$fa-var-toggle-off: \f204; +$fa-var-box-archive: \f187; +$fa-var-archive: \f187; +$fa-var-person-drowning: \e545; +$fa-var-arrow-down-9-1: \f886; +$fa-var-sort-numeric-desc: \f886; +$fa-var-sort-numeric-down-alt: \f886; +$fa-var-face-grin-tongue-squint: \f58a; +$fa-var-grin-tongue-squint: \f58a; +$fa-var-spray-can: \f5bd; +$fa-var-truck-monster: \f63b; +$fa-var-w: \57; +$fa-var-earth-africa: \f57c; +$fa-var-globe-africa: \f57c; +$fa-var-rainbow: \f75b; +$fa-var-circle-notch: \f1ce; +$fa-var-tablet-screen-button: \f3fa; +$fa-var-tablet-alt: \f3fa; +$fa-var-paw: \f1b0; +$fa-var-cloud: \f0c2; +$fa-var-trowel-bricks: \e58a; +$fa-var-face-flushed: \f579; +$fa-var-flushed: \f579; +$fa-var-hospital-user: \f80d; +$fa-var-tent-arrow-left-right: \e57f; +$fa-var-gavel: \f0e3; +$fa-var-legal: \f0e3; +$fa-var-binoculars: \f1e5; +$fa-var-microphone-slash: \f131; +$fa-var-box-tissue: \e05b; +$fa-var-motorcycle: \f21c; +$fa-var-bell-concierge: \f562; +$fa-var-concierge-bell: \f562; +$fa-var-pen-ruler: \f5ae; +$fa-var-pencil-ruler: \f5ae; +$fa-var-people-arrows: \e068; +$fa-var-people-arrows-left-right: \e068; +$fa-var-mars-and-venus-burst: \e523; +$fa-var-square-caret-right: \f152; +$fa-var-caret-square-right: \f152; +$fa-var-scissors: \f0c4; +$fa-var-cut: \f0c4; +$fa-var-sun-plant-wilt: \e57a; +$fa-var-toilets-portable: \e584; +$fa-var-hockey-puck: \f453; +$fa-var-table: \f0ce; +$fa-var-magnifying-glass-arrow-right: \e521; +$fa-var-tachograph-digital: \f566; +$fa-var-digital-tachograph: \f566; +$fa-var-users-slash: \e073; +$fa-var-clover: \e139; +$fa-var-reply: \f3e5; +$fa-var-mail-reply: \f3e5; +$fa-var-star-and-crescent: \f699; +$fa-var-house-fire: \e50c; +$fa-var-square-minus: \f146; +$fa-var-minus-square: \f146; +$fa-var-helicopter: \f533; +$fa-var-compass: \f14e; +$fa-var-square-caret-down: \f150; +$fa-var-caret-square-down: \f150; +$fa-var-file-circle-question: \e4ef; +$fa-var-laptop-code: \f5fc; +$fa-var-swatchbook: \f5c3; +$fa-var-prescription-bottle: \f485; +$fa-var-bars: \f0c9; +$fa-var-navicon: \f0c9; +$fa-var-people-group: \e533; +$fa-var-hourglass-end: \f253; +$fa-var-hourglass-3: \f253; +$fa-var-heart-crack: \f7a9; +$fa-var-heart-broken: \f7a9; +$fa-var-square-up-right: \f360; +$fa-var-external-link-square-alt: \f360; +$fa-var-face-kiss-beam: \f597; +$fa-var-kiss-beam: \f597; +$fa-var-film: \f008; +$fa-var-ruler-horizontal: \f547; +$fa-var-people-robbery: \e536; +$fa-var-lightbulb: \f0eb; +$fa-var-caret-left: \f0d9; +$fa-var-circle-exclamation: \f06a; +$fa-var-exclamation-circle: \f06a; +$fa-var-school-circle-xmark: \e56d; +$fa-var-arrow-right-from-bracket: \f08b; +$fa-var-sign-out: \f08b; +$fa-var-circle-chevron-down: \f13a; +$fa-var-chevron-circle-down: \f13a; +$fa-var-unlock-keyhole: \f13e; +$fa-var-unlock-alt: \f13e; +$fa-var-cloud-showers-heavy: \f740; +$fa-var-headphones-simple: \f58f; +$fa-var-headphones-alt: \f58f; +$fa-var-sitemap: \f0e8; +$fa-var-circle-dollar-to-slot: \f4b9; +$fa-var-donate: \f4b9; +$fa-var-memory: \f538; +$fa-var-road-spikes: \e568; +$fa-var-fire-burner: \e4f1; +$fa-var-flag: \f024; +$fa-var-hanukiah: \f6e6; +$fa-var-feather: \f52d; +$fa-var-volume-low: \f027; +$fa-var-volume-down: \f027; +$fa-var-comment-slash: \f4b3; +$fa-var-cloud-sun-rain: \f743; +$fa-var-compress: \f066; +$fa-var-wheat-awn: \e2cd; +$fa-var-wheat-alt: \e2cd; +$fa-var-ankh: \f644; +$fa-var-hands-holding-child: \e4fa; +$fa-var-asterisk: \2a; +$fa-var-square-check: \f14a; +$fa-var-check-square: \f14a; +$fa-var-peseta-sign: \e221; +$fa-var-heading: \f1dc; +$fa-var-header: \f1dc; +$fa-var-ghost: \f6e2; +$fa-var-list: \f03a; +$fa-var-list-squares: \f03a; +$fa-var-square-phone-flip: \f87b; +$fa-var-phone-square-alt: \f87b; +$fa-var-cart-plus: \f217; +$fa-var-gamepad: \f11b; +$fa-var-circle-dot: \f192; +$fa-var-dot-circle: \f192; +$fa-var-face-dizzy: \f567; +$fa-var-dizzy: \f567; +$fa-var-egg: \f7fb; +$fa-var-house-medical-circle-xmark: \e513; +$fa-var-campground: \f6bb; +$fa-var-folder-plus: \f65e; +$fa-var-futbol: \f1e3; +$fa-var-futbol-ball: \f1e3; +$fa-var-soccer-ball: \f1e3; +$fa-var-paintbrush: \f1fc; +$fa-var-paint-brush: \f1fc; +$fa-var-lock: \f023; +$fa-var-gas-pump: \f52f; +$fa-var-hot-tub-person: \f593; +$fa-var-hot-tub: \f593; +$fa-var-map-location: \f59f; +$fa-var-map-marked: \f59f; +$fa-var-house-flood-water: \e50e; +$fa-var-tree: \f1bb; +$fa-var-bridge-lock: \e4cc; +$fa-var-sack-dollar: \f81d; +$fa-var-pen-to-square: \f044; +$fa-var-edit: \f044; +$fa-var-car-side: \f5e4; +$fa-var-share-nodes: \f1e0; +$fa-var-share-alt: \f1e0; +$fa-var-heart-circle-minus: \e4ff; +$fa-var-hourglass-half: \f252; +$fa-var-hourglass-2: \f252; +$fa-var-microscope: \f610; +$fa-var-sink: \e06d; +$fa-var-bag-shopping: \f290; +$fa-var-shopping-bag: \f290; +$fa-var-arrow-down-z-a: \f881; +$fa-var-sort-alpha-desc: \f881; +$fa-var-sort-alpha-down-alt: \f881; +$fa-var-mitten: \f7b5; +$fa-var-person-rays: \e54d; +$fa-var-users: \f0c0; +$fa-var-eye-slash: \f070; +$fa-var-flask-vial: \e4f3; +$fa-var-hand: \f256; +$fa-var-hand-paper: \f256; +$fa-var-om: \f679; +$fa-var-worm: \e599; +$fa-var-house-circle-xmark: \e50b; +$fa-var-plug: \f1e6; +$fa-var-chevron-up: \f077; +$fa-var-hand-spock: \f259; +$fa-var-stopwatch: \f2f2; +$fa-var-face-kiss: \f596; +$fa-var-kiss: \f596; +$fa-var-bridge-circle-xmark: \e4cb; +$fa-var-face-grin-tongue: \f589; +$fa-var-grin-tongue: \f589; +$fa-var-chess-bishop: \f43a; +$fa-var-face-grin-wink: \f58c; +$fa-var-grin-wink: \f58c; +$fa-var-ear-deaf: \f2a4; +$fa-var-deaf: \f2a4; +$fa-var-deafness: \f2a4; +$fa-var-hard-of-hearing: \f2a4; +$fa-var-road-circle-check: \e564; +$fa-var-dice-five: \f523; +$fa-var-square-rss: \f143; +$fa-var-rss-square: \f143; +$fa-var-land-mine-on: \e51b; +$fa-var-i-cursor: \f246; +$fa-var-stamp: \f5bf; +$fa-var-stairs: \e289; +$fa-var-i: \49; +$fa-var-hryvnia-sign: \f6f2; +$fa-var-hryvnia: \f6f2; +$fa-var-pills: \f484; +$fa-var-face-grin-wide: \f581; +$fa-var-grin-alt: \f581; +$fa-var-tooth: \f5c9; +$fa-var-v: \56; +$fa-var-bangladeshi-taka-sign: \e2e6; +$fa-var-bicycle: \f206; +$fa-var-staff-snake: \e579; +$fa-var-rod-asclepius: \e579; +$fa-var-rod-snake: \e579; +$fa-var-staff-aesculapius: \e579; +$fa-var-head-side-cough-slash: \e062; +$fa-var-truck-medical: \f0f9; +$fa-var-ambulance: \f0f9; +$fa-var-wheat-awn-circle-exclamation: \e598; +$fa-var-snowman: \f7d0; +$fa-var-mortar-pestle: \f5a7; +$fa-var-road-barrier: \e562; +$fa-var-school: \f549; +$fa-var-igloo: \f7ae; +$fa-var-joint: \f595; +$fa-var-angle-right: \f105; +$fa-var-horse: \f6f0; +$fa-var-q: \51; +$fa-var-g: \47; +$fa-var-notes-medical: \f481; +$fa-var-temperature-half: \f2c9; +$fa-var-temperature-2: \f2c9; +$fa-var-thermometer-2: \f2c9; +$fa-var-thermometer-half: \f2c9; +$fa-var-dong-sign: \e169; +$fa-var-capsules: \f46b; +$fa-var-poo-storm: \f75a; +$fa-var-poo-bolt: \f75a; +$fa-var-face-frown-open: \f57a; +$fa-var-frown-open: \f57a; +$fa-var-hand-point-up: \f0a6; +$fa-var-money-bill: \f0d6; +$fa-var-bookmark: \f02e; +$fa-var-align-justify: \f039; +$fa-var-umbrella-beach: \f5ca; +$fa-var-helmet-un: \e503; +$fa-var-bullseye: \f140; +$fa-var-bacon: \f7e5; +$fa-var-hand-point-down: \f0a7; +$fa-var-arrow-up-from-bracket: \e09a; +$fa-var-folder: \f07b; +$fa-var-folder-blank: \f07b; +$fa-var-file-waveform: \f478; +$fa-var-file-medical-alt: \f478; +$fa-var-radiation: \f7b9; +$fa-var-chart-simple: \e473; +$fa-var-mars-stroke: \f229; +$fa-var-vial: \f492; +$fa-var-gauge: \f624; +$fa-var-dashboard: \f624; +$fa-var-gauge-med: \f624; +$fa-var-tachometer-alt-average: \f624; +$fa-var-wand-magic-sparkles: \e2ca; +$fa-var-magic-wand-sparkles: \e2ca; +$fa-var-e: \45; +$fa-var-pen-clip: \f305; +$fa-var-pen-alt: \f305; +$fa-var-bridge-circle-exclamation: \e4ca; +$fa-var-user: \f007; +$fa-var-school-circle-check: \e56b; +$fa-var-dumpster: \f793; +$fa-var-van-shuttle: \f5b6; +$fa-var-shuttle-van: \f5b6; +$fa-var-building-user: \e4da; +$fa-var-square-caret-left: \f191; +$fa-var-caret-square-left: \f191; +$fa-var-highlighter: \f591; +$fa-var-key: \f084; +$fa-var-bullhorn: \f0a1; +$fa-var-globe: \f0ac; +$fa-var-synagogue: \f69b; +$fa-var-person-half-dress: \e548; +$fa-var-road-bridge: \e563; +$fa-var-location-arrow: \f124; +$fa-var-c: \43; +$fa-var-tablet-button: \f10a; +$fa-var-building-lock: \e4d6; +$fa-var-pizza-slice: \f818; +$fa-var-money-bill-wave: \f53a; +$fa-var-chart-area: \f1fe; +$fa-var-area-chart: \f1fe; +$fa-var-house-flag: \e50d; +$fa-var-person-circle-minus: \e540; +$fa-var-ban: \f05e; +$fa-var-cancel: \f05e; +$fa-var-camera-rotate: \e0d8; +$fa-var-spray-can-sparkles: \f5d0; +$fa-var-air-freshener: \f5d0; +$fa-var-star: \f005; +$fa-var-repeat: \f363; +$fa-var-cross: \f654; +$fa-var-box: \f466; +$fa-var-venus-mars: \f228; +$fa-var-arrow-pointer: \f245; +$fa-var-mouse-pointer: \f245; +$fa-var-maximize: \f31e; +$fa-var-expand-arrows-alt: \f31e; +$fa-var-charging-station: \f5e7; +$fa-var-shapes: \f61f; +$fa-var-triangle-circle-square: \f61f; +$fa-var-shuffle: \f074; +$fa-var-random: \f074; +$fa-var-person-running: \f70c; +$fa-var-running: \f70c; +$fa-var-mobile-retro: \e527; +$fa-var-grip-lines-vertical: \f7a5; +$fa-var-spider: \f717; +$fa-var-hands-bound: \e4f9; +$fa-var-file-invoice-dollar: \f571; +$fa-var-plane-circle-exclamation: \e556; +$fa-var-x-ray: \f497; +$fa-var-spell-check: \f891; +$fa-var-slash: \f715; +$fa-var-computer-mouse: \f8cc; +$fa-var-mouse: \f8cc; +$fa-var-arrow-right-to-bracket: \f090; +$fa-var-sign-in: \f090; +$fa-var-shop-slash: \e070; +$fa-var-store-alt-slash: \e070; +$fa-var-server: \f233; +$fa-var-virus-covid-slash: \e4a9; +$fa-var-shop-lock: \e4a5; +$fa-var-hourglass-start: \f251; +$fa-var-hourglass-1: \f251; +$fa-var-blender-phone: \f6b6; +$fa-var-building-wheat: \e4db; +$fa-var-person-breastfeeding: \e53a; +$fa-var-right-to-bracket: \f2f6; +$fa-var-sign-in-alt: \f2f6; +$fa-var-venus: \f221; +$fa-var-passport: \f5ab; +$fa-var-heart-pulse: \f21e; +$fa-var-heartbeat: \f21e; +$fa-var-people-carry-box: \f4ce; +$fa-var-people-carry: \f4ce; +$fa-var-temperature-high: \f769; +$fa-var-microchip: \f2db; +$fa-var-crown: \f521; +$fa-var-weight-hanging: \f5cd; +$fa-var-xmarks-lines: \e59a; +$fa-var-file-prescription: \f572; +$fa-var-weight-scale: \f496; +$fa-var-weight: \f496; +$fa-var-user-group: \f500; +$fa-var-user-friends: \f500; +$fa-var-arrow-up-a-z: \f15e; +$fa-var-sort-alpha-up: \f15e; +$fa-var-chess-knight: \f441; +$fa-var-face-laugh-squint: \f59b; +$fa-var-laugh-squint: \f59b; +$fa-var-wheelchair: \f193; +$fa-var-circle-arrow-up: \f0aa; +$fa-var-arrow-circle-up: \f0aa; +$fa-var-toggle-on: \f205; +$fa-var-person-walking: \f554; +$fa-var-walking: \f554; +$fa-var-l: \4c; +$fa-var-fire: \f06d; +$fa-var-bed-pulse: \f487; +$fa-var-procedures: \f487; +$fa-var-shuttle-space: \f197; +$fa-var-space-shuttle: \f197; +$fa-var-face-laugh: \f599; +$fa-var-laugh: \f599; +$fa-var-folder-open: \f07c; +$fa-var-heart-circle-plus: \e500; +$fa-var-code-fork: \e13b; +$fa-var-city: \f64f; +$fa-var-microphone-lines: \f3c9; +$fa-var-microphone-alt: \f3c9; +$fa-var-pepper-hot: \f816; +$fa-var-unlock: \f09c; +$fa-var-colon-sign: \e140; +$fa-var-headset: \f590; +$fa-var-store-slash: \e071; +$fa-var-road-circle-xmark: \e566; +$fa-var-user-minus: \f503; +$fa-var-mars-stroke-up: \f22a; +$fa-var-mars-stroke-v: \f22a; +$fa-var-champagne-glasses: \f79f; +$fa-var-glass-cheers: \f79f; +$fa-var-clipboard: \f328; +$fa-var-house-circle-exclamation: \e50a; +$fa-var-file-arrow-up: \f574; +$fa-var-file-upload: \f574; +$fa-var-wifi: \f1eb; +$fa-var-wifi-3: \f1eb; +$fa-var-wifi-strong: \f1eb; +$fa-var-bath: \f2cd; +$fa-var-bathtub: \f2cd; +$fa-var-underline: \f0cd; +$fa-var-user-pen: \f4ff; +$fa-var-user-edit: \f4ff; +$fa-var-signature: \f5b7; +$fa-var-stroopwafel: \f551; +$fa-var-bold: \f032; +$fa-var-anchor-lock: \e4ad; +$fa-var-building-ngo: \e4d7; +$fa-var-manat-sign: \e1d5; +$fa-var-not-equal: \f53e; +$fa-var-border-top-left: \f853; +$fa-var-border-style: \f853; +$fa-var-map-location-dot: \f5a0; +$fa-var-map-marked-alt: \f5a0; +$fa-var-jedi: \f669; +$fa-var-square-poll-vertical: \f681; +$fa-var-poll: \f681; +$fa-var-mug-hot: \f7b6; +$fa-var-car-battery: \f5df; +$fa-var-battery-car: \f5df; +$fa-var-gift: \f06b; +$fa-var-dice-two: \f528; +$fa-var-chess-queen: \f445; +$fa-var-glasses: \f530; +$fa-var-chess-board: \f43c; +$fa-var-building-circle-check: \e4d2; +$fa-var-person-chalkboard: \e53d; +$fa-var-mars-stroke-right: \f22b; +$fa-var-mars-stroke-h: \f22b; +$fa-var-hand-back-fist: \f255; +$fa-var-hand-rock: \f255; +$fa-var-square-caret-up: \f151; +$fa-var-caret-square-up: \f151; +$fa-var-cloud-showers-water: \e4e4; +$fa-var-chart-bar: \f080; +$fa-var-bar-chart: \f080; +$fa-var-hands-bubbles: \e05e; +$fa-var-hands-wash: \e05e; +$fa-var-less-than-equal: \f537; +$fa-var-train: \f238; +$fa-var-eye-low-vision: \f2a8; +$fa-var-low-vision: \f2a8; +$fa-var-crow: \f520; +$fa-var-sailboat: \e445; +$fa-var-window-restore: \f2d2; +$fa-var-square-plus: \f0fe; +$fa-var-plus-square: \f0fe; +$fa-var-torii-gate: \f6a1; +$fa-var-frog: \f52e; +$fa-var-bucket: \e4cf; +$fa-var-image: \f03e; +$fa-var-microphone: \f130; +$fa-var-cow: \f6c8; +$fa-var-caret-up: \f0d8; +$fa-var-screwdriver: \f54a; +$fa-var-folder-closed: \e185; +$fa-var-house-tsunami: \e515; +$fa-var-square-nfi: \e576; +$fa-var-arrow-up-from-ground-water: \e4b5; +$fa-var-martini-glass: \f57b; +$fa-var-glass-martini-alt: \f57b; +$fa-var-rotate-left: \f2ea; +$fa-var-rotate-back: \f2ea; +$fa-var-rotate-backward: \f2ea; +$fa-var-undo-alt: \f2ea; +$fa-var-table-columns: \f0db; +$fa-var-columns: \f0db; +$fa-var-lemon: \f094; +$fa-var-head-side-mask: \e063; +$fa-var-handshake: \f2b5; +$fa-var-gem: \f3a5; +$fa-var-dolly: \f472; +$fa-var-dolly-box: \f472; +$fa-var-smoking: \f48d; +$fa-var-minimize: \f78c; +$fa-var-compress-arrows-alt: \f78c; +$fa-var-monument: \f5a6; +$fa-var-snowplow: \f7d2; +$fa-var-angles-right: \f101; +$fa-var-angle-double-right: \f101; +$fa-var-cannabis: \f55f; +$fa-var-circle-play: \f144; +$fa-var-play-circle: \f144; +$fa-var-tablets: \f490; +$fa-var-ethernet: \f796; +$fa-var-euro-sign: \f153; +$fa-var-eur: \f153; +$fa-var-euro: \f153; +$fa-var-chair: \f6c0; +$fa-var-circle-check: \f058; +$fa-var-check-circle: \f058; +$fa-var-circle-stop: \f28d; +$fa-var-stop-circle: \f28d; +$fa-var-compass-drafting: \f568; +$fa-var-drafting-compass: \f568; +$fa-var-plate-wheat: \e55a; +$fa-var-icicles: \f7ad; +$fa-var-person-shelter: \e54f; +$fa-var-neuter: \f22c; +$fa-var-id-badge: \f2c1; +$fa-var-marker: \f5a1; +$fa-var-face-laugh-beam: \f59a; +$fa-var-laugh-beam: \f59a; +$fa-var-helicopter-symbol: \e502; +$fa-var-universal-access: \f29a; +$fa-var-circle-chevron-up: \f139; +$fa-var-chevron-circle-up: \f139; +$fa-var-lari-sign: \e1c8; +$fa-var-volcano: \f770; +$fa-var-person-walking-dashed-line-arrow-right: \e553; +$fa-var-sterling-sign: \f154; +$fa-var-gbp: \f154; +$fa-var-pound-sign: \f154; +$fa-var-viruses: \e076; +$fa-var-square-person-confined: \e577; +$fa-var-user-tie: \f508; +$fa-var-arrow-down-long: \f175; +$fa-var-long-arrow-down: \f175; +$fa-var-tent-arrow-down-to-line: \e57e; +$fa-var-certificate: \f0a3; +$fa-var-reply-all: \f122; +$fa-var-mail-reply-all: \f122; +$fa-var-suitcase: \f0f2; +$fa-var-person-skating: \f7c5; +$fa-var-skating: \f7c5; +$fa-var-filter-circle-dollar: \f662; +$fa-var-funnel-dollar: \f662; +$fa-var-camera-retro: \f083; +$fa-var-circle-arrow-down: \f0ab; +$fa-var-arrow-circle-down: \f0ab; +$fa-var-file-import: \f56f; +$fa-var-arrow-right-to-file: \f56f; +$fa-var-square-arrow-up-right: \f14c; +$fa-var-external-link-square: \f14c; +$fa-var-box-open: \f49e; +$fa-var-scroll: \f70e; +$fa-var-spa: \f5bb; +$fa-var-location-pin-lock: \e51f; +$fa-var-pause: \f04c; +$fa-var-hill-avalanche: \e507; +$fa-var-temperature-empty: \f2cb; +$fa-var-temperature-0: \f2cb; +$fa-var-thermometer-0: \f2cb; +$fa-var-thermometer-empty: \f2cb; +$fa-var-bomb: \f1e2; +$fa-var-registered: \f25d; +$fa-var-address-card: \f2bb; +$fa-var-contact-card: \f2bb; +$fa-var-vcard: \f2bb; +$fa-var-scale-unbalanced-flip: \f516; +$fa-var-balance-scale-right: \f516; +$fa-var-subscript: \f12c; +$fa-var-diamond-turn-right: \f5eb; +$fa-var-directions: \f5eb; +$fa-var-burst: \e4dc; +$fa-var-house-laptop: \e066; +$fa-var-laptop-house: \e066; +$fa-var-face-tired: \f5c8; +$fa-var-tired: \f5c8; +$fa-var-money-bills: \e1f3; +$fa-var-smog: \f75f; +$fa-var-crutch: \f7f7; +$fa-var-cloud-arrow-up: \f0ee; +$fa-var-cloud-upload: \f0ee; +$fa-var-cloud-upload-alt: \f0ee; +$fa-var-palette: \f53f; +$fa-var-arrows-turn-right: \e4c0; +$fa-var-vest: \e085; +$fa-var-ferry: \e4ea; +$fa-var-arrows-down-to-people: \e4b9; +$fa-var-seedling: \f4d8; +$fa-var-sprout: \f4d8; +$fa-var-left-right: \f337; +$fa-var-arrows-alt-h: \f337; +$fa-var-boxes-packing: \e4c7; +$fa-var-circle-arrow-left: \f0a8; +$fa-var-arrow-circle-left: \f0a8; +$fa-var-group-arrows-rotate: \e4f6; +$fa-var-bowl-food: \e4c6; +$fa-var-candy-cane: \f786; +$fa-var-arrow-down-wide-short: \f160; +$fa-var-sort-amount-asc: \f160; +$fa-var-sort-amount-down: \f160; +$fa-var-cloud-bolt: \f76c; +$fa-var-thunderstorm: \f76c; +$fa-var-text-slash: \f87d; +$fa-var-remove-format: \f87d; +$fa-var-face-smile-wink: \f4da; +$fa-var-smile-wink: \f4da; +$fa-var-file-word: \f1c2; +$fa-var-file-powerpoint: \f1c4; +$fa-var-arrows-left-right: \f07e; +$fa-var-arrows-h: \f07e; +$fa-var-house-lock: \e510; +$fa-var-cloud-arrow-down: \f0ed; +$fa-var-cloud-download: \f0ed; +$fa-var-cloud-download-alt: \f0ed; +$fa-var-children: \e4e1; +$fa-var-chalkboard: \f51b; +$fa-var-blackboard: \f51b; +$fa-var-user-large-slash: \f4fa; +$fa-var-user-alt-slash: \f4fa; +$fa-var-envelope-open: \f2b6; +$fa-var-handshake-simple-slash: \e05f; +$fa-var-handshake-alt-slash: \e05f; +$fa-var-mattress-pillow: \e525; +$fa-var-guarani-sign: \e19a; +$fa-var-arrows-rotate: \f021; +$fa-var-refresh: \f021; +$fa-var-sync: \f021; +$fa-var-fire-extinguisher: \f134; +$fa-var-cruzeiro-sign: \e152; +$fa-var-greater-than-equal: \f532; +$fa-var-shield-halved: \f3ed; +$fa-var-shield-alt: \f3ed; +$fa-var-book-atlas: \f558; +$fa-var-atlas: \f558; +$fa-var-virus: \e074; +$fa-var-envelope-circle-check: \e4e8; +$fa-var-layer-group: \f5fd; +$fa-var-arrows-to-dot: \e4be; +$fa-var-archway: \f557; +$fa-var-heart-circle-check: \e4fd; +$fa-var-house-chimney-crack: \f6f1; +$fa-var-house-damage: \f6f1; +$fa-var-file-zipper: \f1c6; +$fa-var-file-archive: \f1c6; +$fa-var-square: \f0c8; +$fa-var-martini-glass-empty: \f000; +$fa-var-glass-martini: \f000; +$fa-var-couch: \f4b8; +$fa-var-cedi-sign: \e0df; +$fa-var-italic: \f033; +$fa-var-church: \f51d; +$fa-var-comments-dollar: \f653; +$fa-var-democrat: \f747; +$fa-var-z: \5a; +$fa-var-person-skiing: \f7c9; +$fa-var-skiing: \f7c9; +$fa-var-road-lock: \e567; +$fa-var-a: \41; +$fa-var-temperature-arrow-down: \e03f; +$fa-var-temperature-down: \e03f; +$fa-var-feather-pointed: \f56b; +$fa-var-feather-alt: \f56b; +$fa-var-p: \50; +$fa-var-snowflake: \f2dc; +$fa-var-newspaper: \f1ea; +$fa-var-rectangle-ad: \f641; +$fa-var-ad: \f641; +$fa-var-circle-arrow-right: \f0a9; +$fa-var-arrow-circle-right: \f0a9; +$fa-var-filter-circle-xmark: \e17b; +$fa-var-locust: \e520; +$fa-var-sort: \f0dc; +$fa-var-unsorted: \f0dc; +$fa-var-list-ol: \f0cb; +$fa-var-list-1-2: \f0cb; +$fa-var-list-numeric: \f0cb; +$fa-var-person-dress-burst: \e544; +$fa-var-money-check-dollar: \f53d; +$fa-var-money-check-alt: \f53d; +$fa-var-vector-square: \f5cb; +$fa-var-bread-slice: \f7ec; +$fa-var-language: \f1ab; +$fa-var-face-kiss-wink-heart: \f598; +$fa-var-kiss-wink-heart: \f598; +$fa-var-filter: \f0b0; +$fa-var-question: \3f; +$fa-var-file-signature: \f573; +$fa-var-up-down-left-right: \f0b2; +$fa-var-arrows-alt: \f0b2; +$fa-var-house-chimney-user: \e065; +$fa-var-hand-holding-heart: \f4be; +$fa-var-puzzle-piece: \f12e; +$fa-var-money-check: \f53c; +$fa-var-star-half-stroke: \f5c0; +$fa-var-star-half-alt: \f5c0; +$fa-var-code: \f121; +$fa-var-whiskey-glass: \f7a0; +$fa-var-glass-whiskey: \f7a0; +$fa-var-building-circle-exclamation: \e4d3; +$fa-var-magnifying-glass-chart: \e522; +$fa-var-arrow-up-right-from-square: \f08e; +$fa-var-external-link: \f08e; +$fa-var-cubes-stacked: \e4e6; +$fa-var-won-sign: \f159; +$fa-var-krw: \f159; +$fa-var-won: \f159; +$fa-var-virus-covid: \e4a8; +$fa-var-austral-sign: \e0a9; +$fa-var-f: \46; +$fa-var-leaf: \f06c; +$fa-var-road: \f018; +$fa-var-taxi: \f1ba; +$fa-var-cab: \f1ba; +$fa-var-person-circle-plus: \e541; +$fa-var-chart-pie: \f200; +$fa-var-pie-chart: \f200; +$fa-var-bolt-lightning: \e0b7; +$fa-var-sack-xmark: \e56a; +$fa-var-file-excel: \f1c3; +$fa-var-file-contract: \f56c; +$fa-var-fish-fins: \e4f2; +$fa-var-building-flag: \e4d5; +$fa-var-face-grin-beam: \f582; +$fa-var-grin-beam: \f582; +$fa-var-object-ungroup: \f248; +$fa-var-poop: \f619; +$fa-var-location-pin: \f041; +$fa-var-map-marker: \f041; +$fa-var-kaaba: \f66b; +$fa-var-toilet-paper: \f71e; +$fa-var-helmet-safety: \f807; +$fa-var-hard-hat: \f807; +$fa-var-hat-hard: \f807; +$fa-var-eject: \f052; +$fa-var-circle-right: \f35a; +$fa-var-arrow-alt-circle-right: \f35a; +$fa-var-plane-circle-check: \e555; +$fa-var-face-rolling-eyes: \f5a5; +$fa-var-meh-rolling-eyes: \f5a5; +$fa-var-object-group: \f247; +$fa-var-chart-line: \f201; +$fa-var-line-chart: \f201; +$fa-var-mask-ventilator: \e524; +$fa-var-arrow-right: \f061; +$fa-var-signs-post: \f277; +$fa-var-map-signs: \f277; +$fa-var-cash-register: \f788; +$fa-var-person-circle-question: \e542; +$fa-var-h: \48; +$fa-var-tarp: \e57b; +$fa-var-screwdriver-wrench: \f7d9; +$fa-var-tools: \f7d9; +$fa-var-arrows-to-eye: \e4bf; +$fa-var-plug-circle-bolt: \e55b; +$fa-var-heart: \f004; +$fa-var-mars-and-venus: \f224; +$fa-var-house-user: \e1b0; +$fa-var-home-user: \e1b0; +$fa-var-dumpster-fire: \f794; +$fa-var-house-crack: \e3b1; +$fa-var-martini-glass-citrus: \f561; +$fa-var-cocktail: \f561; +$fa-var-face-surprise: \f5c2; +$fa-var-surprise: \f5c2; +$fa-var-bottle-water: \e4c5; +$fa-var-circle-pause: \f28b; +$fa-var-pause-circle: \f28b; +$fa-var-toilet-paper-slash: \e072; +$fa-var-apple-whole: \f5d1; +$fa-var-apple-alt: \f5d1; +$fa-var-kitchen-set: \e51a; +$fa-var-r: \52; +$fa-var-temperature-quarter: \f2ca; +$fa-var-temperature-1: \f2ca; +$fa-var-thermometer-1: \f2ca; +$fa-var-thermometer-quarter: \f2ca; +$fa-var-cube: \f1b2; +$fa-var-bitcoin-sign: \e0b4; +$fa-var-shield-dog: \e573; +$fa-var-solar-panel: \f5ba; +$fa-var-lock-open: \f3c1; +$fa-var-elevator: \e16d; +$fa-var-money-bill-transfer: \e528; +$fa-var-money-bill-trend-up: \e529; +$fa-var-house-flood-water-circle-arrow-right: \e50f; +$fa-var-square-poll-horizontal: \f682; +$fa-var-poll-h: \f682; +$fa-var-circle: \f111; +$fa-var-backward-fast: \f049; +$fa-var-fast-backward: \f049; +$fa-var-recycle: \f1b8; +$fa-var-user-astronaut: \f4fb; +$fa-var-plane-slash: \e069; +$fa-var-trademark: \f25c; +$fa-var-basketball: \f434; +$fa-var-basketball-ball: \f434; +$fa-var-satellite-dish: \f7c0; +$fa-var-circle-up: \f35b; +$fa-var-arrow-alt-circle-up: \f35b; +$fa-var-mobile-screen-button: \f3cd; +$fa-var-mobile-alt: \f3cd; +$fa-var-volume-high: \f028; +$fa-var-volume-up: \f028; +$fa-var-users-rays: \e593; +$fa-var-wallet: \f555; +$fa-var-clipboard-check: \f46c; +$fa-var-file-audio: \f1c7; +$fa-var-burger: \f805; +$fa-var-hamburger: \f805; +$fa-var-wrench: \f0ad; +$fa-var-bugs: \e4d0; +$fa-var-rupee-sign: \f156; +$fa-var-rupee: \f156; +$fa-var-file-image: \f1c5; +$fa-var-circle-question: \f059; +$fa-var-question-circle: \f059; +$fa-var-plane-departure: \f5b0; +$fa-var-handshake-slash: \e060; +$fa-var-book-bookmark: \e0bb; +$fa-var-code-branch: \f126; +$fa-var-hat-cowboy: \f8c0; +$fa-var-bridge: \e4c8; +$fa-var-phone-flip: \f879; +$fa-var-phone-alt: \f879; +$fa-var-truck-front: \e2b7; +$fa-var-cat: \f6be; +$fa-var-anchor-circle-exclamation: \e4ab; +$fa-var-truck-field: \e58d; +$fa-var-route: \f4d7; +$fa-var-clipboard-question: \e4e3; +$fa-var-panorama: \e209; +$fa-var-comment-medical: \f7f5; +$fa-var-teeth-open: \f62f; +$fa-var-file-circle-minus: \e4ed; +$fa-var-tags: \f02c; +$fa-var-wine-glass: \f4e3; +$fa-var-forward-fast: \f050; +$fa-var-fast-forward: \f050; +$fa-var-face-meh-blank: \f5a4; +$fa-var-meh-blank: \f5a4; +$fa-var-square-parking: \f540; +$fa-var-parking: \f540; +$fa-var-house-signal: \e012; +$fa-var-bars-progress: \f828; +$fa-var-tasks-alt: \f828; +$fa-var-faucet-drip: \e006; +$fa-var-cart-flatbed: \f474; +$fa-var-dolly-flatbed: \f474; +$fa-var-ban-smoking: \f54d; +$fa-var-smoking-ban: \f54d; +$fa-var-terminal: \f120; +$fa-var-mobile-button: \f10b; +$fa-var-house-medical-flag: \e514; +$fa-var-basket-shopping: \f291; +$fa-var-shopping-basket: \f291; +$fa-var-tape: \f4db; +$fa-var-bus-simple: \f55e; +$fa-var-bus-alt: \f55e; +$fa-var-eye: \f06e; +$fa-var-face-sad-cry: \f5b3; +$fa-var-sad-cry: \f5b3; +$fa-var-audio-description: \f29e; +$fa-var-person-military-to-person: \e54c; +$fa-var-file-shield: \e4f0; +$fa-var-user-slash: \f506; +$fa-var-pen: \f304; +$fa-var-tower-observation: \e586; +$fa-var-file-code: \f1c9; +$fa-var-signal: \f012; +$fa-var-signal-5: \f012; +$fa-var-signal-perfect: \f012; +$fa-var-bus: \f207; +$fa-var-heart-circle-xmark: \e501; +$fa-var-house-chimney: \e3af; +$fa-var-home-lg: \e3af; +$fa-var-window-maximize: \f2d0; +$fa-var-face-frown: \f119; +$fa-var-frown: \f119; +$fa-var-prescription: \f5b1; +$fa-var-shop: \f54f; +$fa-var-store-alt: \f54f; +$fa-var-floppy-disk: \f0c7; +$fa-var-save: \f0c7; +$fa-var-vihara: \f6a7; +$fa-var-scale-unbalanced: \f515; +$fa-var-balance-scale-left: \f515; +$fa-var-sort-up: \f0de; +$fa-var-sort-asc: \f0de; +$fa-var-comment-dots: \f4ad; +$fa-var-commenting: \f4ad; +$fa-var-plant-wilt: \e5aa; +$fa-var-diamond: \f219; +$fa-var-face-grin-squint: \f585; +$fa-var-grin-squint: \f585; +$fa-var-hand-holding-dollar: \f4c0; +$fa-var-hand-holding-usd: \f4c0; +$fa-var-bacterium: \e05a; +$fa-var-hand-pointer: \f25a; +$fa-var-drum-steelpan: \f56a; +$fa-var-hand-scissors: \f257; +$fa-var-hands-praying: \f684; +$fa-var-praying-hands: \f684; +$fa-var-arrow-rotate-right: \f01e; +$fa-var-arrow-right-rotate: \f01e; +$fa-var-arrow-rotate-forward: \f01e; +$fa-var-redo: \f01e; +$fa-var-biohazard: \f780; +$fa-var-location-crosshairs: \f601; +$fa-var-location: \f601; +$fa-var-mars-double: \f227; +$fa-var-child-dress: \e59c; +$fa-var-users-between-lines: \e591; +$fa-var-lungs-virus: \e067; +$fa-var-face-grin-tears: \f588; +$fa-var-grin-tears: \f588; +$fa-var-phone: \f095; +$fa-var-calendar-xmark: \f273; +$fa-var-calendar-times: \f273; +$fa-var-child-reaching: \e59d; +$fa-var-head-side-virus: \e064; +$fa-var-user-gear: \f4fe; +$fa-var-user-cog: \f4fe; +$fa-var-arrow-up-1-9: \f163; +$fa-var-sort-numeric-up: \f163; +$fa-var-door-closed: \f52a; +$fa-var-shield-virus: \e06c; +$fa-var-dice-six: \f526; +$fa-var-mosquito-net: \e52c; +$fa-var-bridge-water: \e4ce; +$fa-var-person-booth: \f756; +$fa-var-text-width: \f035; +$fa-var-hat-wizard: \f6e8; +$fa-var-pen-fancy: \f5ac; +$fa-var-person-digging: \f85e; +$fa-var-digging: \f85e; +$fa-var-trash: \f1f8; +$fa-var-gauge-simple: \f629; +$fa-var-gauge-simple-med: \f629; +$fa-var-tachometer-average: \f629; +$fa-var-book-medical: \f7e6; +$fa-var-poo: \f2fe; +$fa-var-quote-right: \f10e; +$fa-var-quote-right-alt: \f10e; +$fa-var-shirt: \f553; +$fa-var-t-shirt: \f553; +$fa-var-tshirt: \f553; +$fa-var-cubes: \f1b3; +$fa-var-divide: \f529; +$fa-var-tenge-sign: \f7d7; +$fa-var-tenge: \f7d7; +$fa-var-headphones: \f025; +$fa-var-hands-holding: \f4c2; +$fa-var-hands-clapping: \e1a8; +$fa-var-republican: \f75e; +$fa-var-arrow-left: \f060; +$fa-var-person-circle-xmark: \e543; +$fa-var-ruler: \f545; +$fa-var-align-left: \f036; +$fa-var-dice-d6: \f6d1; +$fa-var-restroom: \f7bd; +$fa-var-j: \4a; +$fa-var-users-viewfinder: \e595; +$fa-var-file-video: \f1c8; +$fa-var-up-right-from-square: \f35d; +$fa-var-external-link-alt: \f35d; +$fa-var-table-cells: \f00a; +$fa-var-th: \f00a; +$fa-var-file-pdf: \f1c1; +$fa-var-book-bible: \f647; +$fa-var-bible: \f647; +$fa-var-o: \4f; +$fa-var-suitcase-medical: \f0fa; +$fa-var-medkit: \f0fa; +$fa-var-user-secret: \f21b; +$fa-var-otter: \f700; +$fa-var-person-dress: \f182; +$fa-var-female: \f182; +$fa-var-comment-dollar: \f651; +$fa-var-business-time: \f64a; +$fa-var-briefcase-clock: \f64a; +$fa-var-table-cells-large: \f009; +$fa-var-th-large: \f009; +$fa-var-book-tanakh: \f827; +$fa-var-tanakh: \f827; +$fa-var-phone-volume: \f2a0; +$fa-var-volume-control-phone: \f2a0; +$fa-var-hat-cowboy-side: \f8c1; +$fa-var-clipboard-user: \f7f3; +$fa-var-child: \f1ae; +$fa-var-lira-sign: \f195; +$fa-var-satellite: \f7bf; +$fa-var-plane-lock: \e558; +$fa-var-tag: \f02b; +$fa-var-comment: \f075; +$fa-var-cake-candles: \f1fd; +$fa-var-birthday-cake: \f1fd; +$fa-var-cake: \f1fd; +$fa-var-envelope: \f0e0; +$fa-var-angles-up: \f102; +$fa-var-angle-double-up: \f102; +$fa-var-paperclip: \f0c6; +$fa-var-arrow-right-to-city: \e4b3; +$fa-var-ribbon: \f4d6; +$fa-var-lungs: \f604; +$fa-var-arrow-up-9-1: \f887; +$fa-var-sort-numeric-up-alt: \f887; +$fa-var-litecoin-sign: \e1d3; +$fa-var-border-none: \f850; +$fa-var-circle-nodes: \e4e2; +$fa-var-parachute-box: \f4cd; +$fa-var-indent: \f03c; +$fa-var-truck-field-un: \e58e; +$fa-var-hourglass: \f254; +$fa-var-hourglass-empty: \f254; +$fa-var-mountain: \f6fc; +$fa-var-user-doctor: \f0f0; +$fa-var-user-md: \f0f0; +$fa-var-circle-info: \f05a; +$fa-var-info-circle: \f05a; +$fa-var-cloud-meatball: \f73b; +$fa-var-camera: \f030; +$fa-var-camera-alt: \f030; +$fa-var-square-virus: \e578; +$fa-var-meteor: \f753; +$fa-var-car-on: \e4dd; +$fa-var-sleigh: \f7cc; +$fa-var-arrow-down-1-9: \f162; +$fa-var-sort-numeric-asc: \f162; +$fa-var-sort-numeric-down: \f162; +$fa-var-hand-holding-droplet: \f4c1; +$fa-var-hand-holding-water: \f4c1; +$fa-var-water: \f773; +$fa-var-calendar-check: \f274; +$fa-var-braille: \f2a1; +$fa-var-prescription-bottle-medical: \f486; +$fa-var-prescription-bottle-alt: \f486; +$fa-var-landmark: \f66f; +$fa-var-truck: \f0d1; +$fa-var-crosshairs: \f05b; +$fa-var-person-cane: \e53c; +$fa-var-tent: \e57d; +$fa-var-vest-patches: \e086; +$fa-var-check-double: \f560; +$fa-var-arrow-down-a-z: \f15d; +$fa-var-sort-alpha-asc: \f15d; +$fa-var-sort-alpha-down: \f15d; +$fa-var-money-bill-wheat: \e52a; +$fa-var-cookie: \f563; +$fa-var-arrow-rotate-left: \f0e2; +$fa-var-arrow-left-rotate: \f0e2; +$fa-var-arrow-rotate-back: \f0e2; +$fa-var-arrow-rotate-backward: \f0e2; +$fa-var-undo: \f0e2; +$fa-var-hard-drive: \f0a0; +$fa-var-hdd: \f0a0; +$fa-var-face-grin-squint-tears: \f586; +$fa-var-grin-squint-tears: \f586; +$fa-var-dumbbell: \f44b; +$fa-var-rectangle-list: \f022; +$fa-var-list-alt: \f022; +$fa-var-tarp-droplet: \e57c; +$fa-var-house-medical-circle-check: \e511; +$fa-var-person-skiing-nordic: \f7ca; +$fa-var-skiing-nordic: \f7ca; +$fa-var-calendar-plus: \f271; +$fa-var-plane-arrival: \f5af; +$fa-var-circle-left: \f359; +$fa-var-arrow-alt-circle-left: \f359; +$fa-var-train-subway: \f239; +$fa-var-subway: \f239; +$fa-var-chart-gantt: \e0e4; +$fa-var-indian-rupee-sign: \e1bc; +$fa-var-indian-rupee: \e1bc; +$fa-var-inr: \e1bc; +$fa-var-crop-simple: \f565; +$fa-var-crop-alt: \f565; +$fa-var-money-bill-1: \f3d1; +$fa-var-money-bill-alt: \f3d1; +$fa-var-left-long: \f30a; +$fa-var-long-arrow-alt-left: \f30a; +$fa-var-dna: \f471; +$fa-var-virus-slash: \e075; +$fa-var-minus: \f068; +$fa-var-subtract: \f068; +$fa-var-chess: \f439; +$fa-var-arrow-left-long: \f177; +$fa-var-long-arrow-left: \f177; +$fa-var-plug-circle-check: \e55c; +$fa-var-street-view: \f21d; +$fa-var-franc-sign: \e18f; +$fa-var-volume-off: \f026; +$fa-var-hands-asl-interpreting: \f2a3; +$fa-var-american-sign-language-interpreting: \f2a3; +$fa-var-asl-interpreting: \f2a3; +$fa-var-hands-american-sign-language-interpreting: \f2a3; +$fa-var-gear: \f013; +$fa-var-cog: \f013; +$fa-var-droplet-slash: \f5c7; +$fa-var-tint-slash: \f5c7; +$fa-var-mosque: \f678; +$fa-var-mosquito: \e52b; +$fa-var-star-of-david: \f69a; +$fa-var-person-military-rifle: \e54b; +$fa-var-cart-shopping: \f07a; +$fa-var-shopping-cart: \f07a; +$fa-var-vials: \f493; +$fa-var-plug-circle-plus: \e55f; +$fa-var-place-of-worship: \f67f; +$fa-var-grip-vertical: \f58e; +$fa-var-arrow-turn-up: \f148; +$fa-var-level-up: \f148; +$fa-var-u: \55; +$fa-var-square-root-variable: \f698; +$fa-var-square-root-alt: \f698; +$fa-var-clock: \f017; +$fa-var-clock-four: \f017; +$fa-var-backward-step: \f048; +$fa-var-step-backward: \f048; +$fa-var-pallet: \f482; +$fa-var-faucet: \e005; +$fa-var-baseball-bat-ball: \f432; +$fa-var-s: \53; +$fa-var-timeline: \e29c; +$fa-var-keyboard: \f11c; +$fa-var-caret-down: \f0d7; +$fa-var-house-chimney-medical: \f7f2; +$fa-var-clinic-medical: \f7f2; +$fa-var-temperature-three-quarters: \f2c8; +$fa-var-temperature-3: \f2c8; +$fa-var-thermometer-3: \f2c8; +$fa-var-thermometer-three-quarters: \f2c8; +$fa-var-mobile-screen: \f3cf; +$fa-var-mobile-android-alt: \f3cf; +$fa-var-plane-up: \e22d; +$fa-var-piggy-bank: \f4d3; +$fa-var-battery-half: \f242; +$fa-var-battery-3: \f242; +$fa-var-mountain-city: \e52e; +$fa-var-coins: \f51e; +$fa-var-khanda: \f66d; +$fa-var-sliders: \f1de; +$fa-var-sliders-h: \f1de; +$fa-var-folder-tree: \f802; +$fa-var-network-wired: \f6ff; +$fa-var-map-pin: \f276; +$fa-var-hamsa: \f665; +$fa-var-cent-sign: \e3f5; +$fa-var-flask: \f0c3; +$fa-var-person-pregnant: \e31e; +$fa-var-wand-sparkles: \f72b; +$fa-var-ellipsis-vertical: \f142; +$fa-var-ellipsis-v: \f142; +$fa-var-ticket: \f145; +$fa-var-power-off: \f011; +$fa-var-right-long: \f30b; +$fa-var-long-arrow-alt-right: \f30b; +$fa-var-flag-usa: \f74d; +$fa-var-laptop-file: \e51d; +$fa-var-tty: \f1e4; +$fa-var-teletype: \f1e4; +$fa-var-diagram-next: \e476; +$fa-var-person-rifle: \e54e; +$fa-var-house-medical-circle-exclamation: \e512; +$fa-var-closed-captioning: \f20a; +$fa-var-person-hiking: \f6ec; +$fa-var-hiking: \f6ec; +$fa-var-venus-double: \f226; +$fa-var-images: \f302; +$fa-var-calculator: \f1ec; +$fa-var-people-pulling: \e535; +$fa-var-n: \4e; +$fa-var-cable-car: \f7da; +$fa-var-tram: \f7da; +$fa-var-cloud-rain: \f73d; +$fa-var-building-circle-xmark: \e4d4; +$fa-var-ship: \f21a; +$fa-var-arrows-down-to-line: \e4b8; +$fa-var-download: \f019; +$fa-var-face-grin: \f580; +$fa-var-grin: \f580; +$fa-var-delete-left: \f55a; +$fa-var-backspace: \f55a; +$fa-var-eye-dropper: \f1fb; +$fa-var-eye-dropper-empty: \f1fb; +$fa-var-eyedropper: \f1fb; +$fa-var-file-circle-check: \e5a0; +$fa-var-forward: \f04e; +$fa-var-mobile: \f3ce; +$fa-var-mobile-android: \f3ce; +$fa-var-mobile-phone: \f3ce; +$fa-var-face-meh: \f11a; +$fa-var-meh: \f11a; +$fa-var-align-center: \f037; +$fa-var-book-skull: \f6b7; +$fa-var-book-dead: \f6b7; +$fa-var-id-card: \f2c2; +$fa-var-drivers-license: \f2c2; +$fa-var-outdent: \f03b; +$fa-var-dedent: \f03b; +$fa-var-heart-circle-exclamation: \e4fe; +$fa-var-house: \f015; +$fa-var-home: \f015; +$fa-var-home-alt: \f015; +$fa-var-home-lg-alt: \f015; +$fa-var-calendar-week: \f784; +$fa-var-laptop-medical: \f812; +$fa-var-b: \42; +$fa-var-file-medical: \f477; +$fa-var-dice-one: \f525; +$fa-var-kiwi-bird: \f535; +$fa-var-arrow-right-arrow-left: \f0ec; +$fa-var-exchange: \f0ec; +$fa-var-rotate-right: \f2f9; +$fa-var-redo-alt: \f2f9; +$fa-var-rotate-forward: \f2f9; +$fa-var-utensils: \f2e7; +$fa-var-cutlery: \f2e7; +$fa-var-arrow-up-wide-short: \f161; +$fa-var-sort-amount-up: \f161; +$fa-var-mill-sign: \e1ed; +$fa-var-bowl-rice: \e2eb; +$fa-var-skull: \f54c; +$fa-var-tower-broadcast: \f519; +$fa-var-broadcast-tower: \f519; +$fa-var-truck-pickup: \f63c; +$fa-var-up-long: \f30c; +$fa-var-long-arrow-alt-up: \f30c; +$fa-var-stop: \f04d; +$fa-var-code-merge: \f387; +$fa-var-upload: \f093; +$fa-var-hurricane: \f751; +$fa-var-mound: \e52d; +$fa-var-toilet-portable: \e583; +$fa-var-compact-disc: \f51f; +$fa-var-file-arrow-down: \f56d; +$fa-var-file-download: \f56d; +$fa-var-caravan: \f8ff; +$fa-var-shield-cat: \e572; +$fa-var-bolt: \f0e7; +$fa-var-zap: \f0e7; +$fa-var-glass-water: \e4f4; +$fa-var-oil-well: \e532; +$fa-var-vault: \e2c5; +$fa-var-mars: \f222; +$fa-var-toilet: \f7d8; +$fa-var-plane-circle-xmark: \e557; +$fa-var-yen-sign: \f157; +$fa-var-cny: \f157; +$fa-var-jpy: \f157; +$fa-var-rmb: \f157; +$fa-var-yen: \f157; +$fa-var-ruble-sign: \f158; +$fa-var-rouble: \f158; +$fa-var-rub: \f158; +$fa-var-ruble: \f158; +$fa-var-sun: \f185; +$fa-var-guitar: \f7a6; +$fa-var-face-laugh-wink: \f59c; +$fa-var-laugh-wink: \f59c; +$fa-var-horse-head: \f7ab; +$fa-var-bore-hole: \e4c3; +$fa-var-industry: \f275; +$fa-var-circle-down: \f358; +$fa-var-arrow-alt-circle-down: \f358; +$fa-var-arrows-turn-to-dots: \e4c1; +$fa-var-florin-sign: \e184; +$fa-var-arrow-down-short-wide: \f884; +$fa-var-sort-amount-desc: \f884; +$fa-var-sort-amount-down-alt: \f884; +$fa-var-less-than: \3c; +$fa-var-angle-down: \f107; +$fa-var-car-tunnel: \e4de; +$fa-var-head-side-cough: \e061; +$fa-var-grip-lines: \f7a4; +$fa-var-thumbs-down: \f165; +$fa-var-user-lock: \f502; +$fa-var-arrow-right-long: \f178; +$fa-var-long-arrow-right: \f178; +$fa-var-anchor-circle-xmark: \e4ac; +$fa-var-ellipsis: \f141; +$fa-var-ellipsis-h: \f141; +$fa-var-chess-pawn: \f443; +$fa-var-kit-medical: \f479; +$fa-var-first-aid: \f479; +$fa-var-person-through-window: \e5a9; +$fa-var-toolbox: \f552; +$fa-var-hands-holding-circle: \e4fb; +$fa-var-bug: \f188; +$fa-var-credit-card: \f09d; +$fa-var-credit-card-alt: \f09d; +$fa-var-car: \f1b9; +$fa-var-automobile: \f1b9; +$fa-var-hand-holding-hand: \e4f7; +$fa-var-book-open-reader: \f5da; +$fa-var-book-reader: \f5da; +$fa-var-mountain-sun: \e52f; +$fa-var-arrows-left-right-to-line: \e4ba; +$fa-var-dice-d20: \f6cf; +$fa-var-truck-droplet: \e58c; +$fa-var-file-circle-xmark: \e5a1; +$fa-var-temperature-arrow-up: \e040; +$fa-var-temperature-up: \e040; +$fa-var-medal: \f5a2; +$fa-var-bed: \f236; +$fa-var-square-h: \f0fd; +$fa-var-h-square: \f0fd; +$fa-var-podcast: \f2ce; +$fa-var-temperature-full: \f2c7; +$fa-var-temperature-4: \f2c7; +$fa-var-thermometer-4: \f2c7; +$fa-var-thermometer-full: \f2c7; +$fa-var-bell: \f0f3; +$fa-var-superscript: \f12b; +$fa-var-plug-circle-xmark: \e560; +$fa-var-star-of-life: \f621; +$fa-var-phone-slash: \f3dd; +$fa-var-paint-roller: \f5aa; +$fa-var-handshake-angle: \f4c4; +$fa-var-hands-helping: \f4c4; +$fa-var-location-dot: \f3c5; +$fa-var-map-marker-alt: \f3c5; +$fa-var-file: \f15b; +$fa-var-greater-than: \3e; +$fa-var-person-swimming: \f5c4; +$fa-var-swimmer: \f5c4; +$fa-var-arrow-down: \f063; +$fa-var-droplet: \f043; +$fa-var-tint: \f043; +$fa-var-eraser: \f12d; +$fa-var-earth-americas: \f57d; +$fa-var-earth: \f57d; +$fa-var-earth-america: \f57d; +$fa-var-globe-americas: \f57d; +$fa-var-person-burst: \e53b; +$fa-var-dove: \f4ba; +$fa-var-battery-empty: \f244; +$fa-var-battery-0: \f244; +$fa-var-socks: \f696; +$fa-var-inbox: \f01c; +$fa-var-section: \e447; +$fa-var-gauge-high: \f625; +$fa-var-tachometer-alt: \f625; +$fa-var-tachometer-alt-fast: \f625; +$fa-var-envelope-open-text: \f658; +$fa-var-hospital: \f0f8; +$fa-var-hospital-alt: \f0f8; +$fa-var-hospital-wide: \f0f8; +$fa-var-wine-bottle: \f72f; +$fa-var-chess-rook: \f447; +$fa-var-bars-staggered: \f550; +$fa-var-reorder: \f550; +$fa-var-stream: \f550; +$fa-var-dharmachakra: \f655; +$fa-var-hotdog: \f80f; +$fa-var-person-walking-with-cane: \f29d; +$fa-var-blind: \f29d; +$fa-var-drum: \f569; +$fa-var-ice-cream: \f810; +$fa-var-heart-circle-bolt: \e4fc; +$fa-var-fax: \f1ac; +$fa-var-paragraph: \f1dd; +$fa-var-check-to-slot: \f772; +$fa-var-vote-yea: \f772; +$fa-var-star-half: \f089; +$fa-var-boxes-stacked: \f468; +$fa-var-boxes: \f468; +$fa-var-boxes-alt: \f468; +$fa-var-link: \f0c1; +$fa-var-chain: \f0c1; +$fa-var-ear-listen: \f2a2; +$fa-var-assistive-listening-systems: \f2a2; +$fa-var-tree-city: \e587; +$fa-var-play: \f04b; +$fa-var-font: \f031; +$fa-var-rupiah-sign: \e23d; +$fa-var-magnifying-glass: \f002; +$fa-var-search: \f002; +$fa-var-table-tennis-paddle-ball: \f45d; +$fa-var-ping-pong-paddle-ball: \f45d; +$fa-var-table-tennis: \f45d; +$fa-var-person-dots-from-line: \f470; +$fa-var-diagnoses: \f470; +$fa-var-trash-can-arrow-up: \f82a; +$fa-var-trash-restore-alt: \f82a; +$fa-var-naira-sign: \e1f6; +$fa-var-cart-arrow-down: \f218; +$fa-var-walkie-talkie: \f8ef; +$fa-var-file-pen: \f31c; +$fa-var-file-edit: \f31c; +$fa-var-receipt: \f543; +$fa-var-square-pen: \f14b; +$fa-var-pen-square: \f14b; +$fa-var-pencil-square: \f14b; +$fa-var-suitcase-rolling: \f5c1; +$fa-var-person-circle-exclamation: \e53f; +$fa-var-chevron-down: \f078; +$fa-var-battery-full: \f240; +$fa-var-battery: \f240; +$fa-var-battery-5: \f240; +$fa-var-skull-crossbones: \f714; +$fa-var-code-compare: \e13a; +$fa-var-list-ul: \f0ca; +$fa-var-list-dots: \f0ca; +$fa-var-school-lock: \e56f; +$fa-var-tower-cell: \e585; +$fa-var-down-long: \f309; +$fa-var-long-arrow-alt-down: \f309; +$fa-var-ranking-star: \e561; +$fa-var-chess-king: \f43f; +$fa-var-person-harassing: \e549; +$fa-var-brazilian-real-sign: \e46c; +$fa-var-landmark-dome: \f752; +$fa-var-landmark-alt: \f752; +$fa-var-arrow-up: \f062; +$fa-var-tv: \f26c; +$fa-var-television: \f26c; +$fa-var-tv-alt: \f26c; +$fa-var-shrimp: \e448; +$fa-var-list-check: \f0ae; +$fa-var-tasks: \f0ae; +$fa-var-jug-detergent: \e519; +$fa-var-circle-user: \f2bd; +$fa-var-user-circle: \f2bd; +$fa-var-user-shield: \f505; +$fa-var-wind: \f72e; +$fa-var-car-burst: \f5e1; +$fa-var-car-crash: \f5e1; +$fa-var-y: \59; +$fa-var-person-snowboarding: \f7ce; +$fa-var-snowboarding: \f7ce; +$fa-var-truck-fast: \f48b; +$fa-var-shipping-fast: \f48b; +$fa-var-fish: \f578; +$fa-var-user-graduate: \f501; +$fa-var-circle-half-stroke: \f042; +$fa-var-adjust: \f042; +$fa-var-clapperboard: \e131; +$fa-var-circle-radiation: \f7ba; +$fa-var-radiation-alt: \f7ba; +$fa-var-baseball: \f433; +$fa-var-baseball-ball: \f433; +$fa-var-jet-fighter-up: \e518; +$fa-var-diagram-project: \f542; +$fa-var-project-diagram: \f542; +$fa-var-copy: \f0c5; +$fa-var-volume-xmark: \f6a9; +$fa-var-volume-mute: \f6a9; +$fa-var-volume-times: \f6a9; +$fa-var-hand-sparkles: \e05d; +$fa-var-grip: \f58d; +$fa-var-grip-horizontal: \f58d; +$fa-var-share-from-square: \f14d; +$fa-var-share-square: \f14d; +$fa-var-child-combatant: \e4e0; +$fa-var-child-rifle: \e4e0; +$fa-var-gun: \e19b; +$fa-var-square-phone: \f098; +$fa-var-phone-square: \f098; +$fa-var-plus: \2b; +$fa-var-add: \2b; +$fa-var-expand: \f065; +$fa-var-computer: \e4e5; +$fa-var-xmark: \f00d; +$fa-var-close: \f00d; +$fa-var-multiply: \f00d; +$fa-var-remove: \f00d; +$fa-var-times: \f00d; +$fa-var-arrows-up-down-left-right: \f047; +$fa-var-arrows: \f047; +$fa-var-chalkboard-user: \f51c; +$fa-var-chalkboard-teacher: \f51c; +$fa-var-peso-sign: \e222; +$fa-var-building-shield: \e4d8; +$fa-var-baby: \f77c; +$fa-var-users-line: \e592; +$fa-var-quote-left: \f10d; +$fa-var-quote-left-alt: \f10d; +$fa-var-tractor: \f722; +$fa-var-trash-arrow-up: \f829; +$fa-var-trash-restore: \f829; +$fa-var-arrow-down-up-lock: \e4b0; +$fa-var-lines-leaning: \e51e; +$fa-var-ruler-combined: \f546; +$fa-var-copyright: \f1f9; +$fa-var-equals: \3d; +$fa-var-blender: \f517; +$fa-var-teeth: \f62e; +$fa-var-shekel-sign: \f20b; +$fa-var-ils: \f20b; +$fa-var-shekel: \f20b; +$fa-var-sheqel: \f20b; +$fa-var-sheqel-sign: \f20b; +$fa-var-map: \f279; +$fa-var-rocket: \f135; +$fa-var-photo-film: \f87c; +$fa-var-photo-video: \f87c; +$fa-var-folder-minus: \f65d; +$fa-var-store: \f54e; +$fa-var-arrow-trend-up: \e098; +$fa-var-plug-circle-minus: \e55e; +$fa-var-sign-hanging: \f4d9; +$fa-var-sign: \f4d9; +$fa-var-bezier-curve: \f55b; +$fa-var-bell-slash: \f1f6; +$fa-var-tablet: \f3fb; +$fa-var-tablet-android: \f3fb; +$fa-var-school-flag: \e56e; +$fa-var-fill: \f575; +$fa-var-angle-up: \f106; +$fa-var-drumstick-bite: \f6d7; +$fa-var-holly-berry: \f7aa; +$fa-var-chevron-left: \f053; +$fa-var-bacteria: \e059; +$fa-var-hand-lizard: \f258; +$fa-var-notdef: \e1fe; +$fa-var-disease: \f7fa; +$fa-var-briefcase-medical: \f469; +$fa-var-genderless: \f22d; +$fa-var-chevron-right: \f054; +$fa-var-retweet: \f079; +$fa-var-car-rear: \f5de; +$fa-var-car-alt: \f5de; +$fa-var-pump-soap: \e06b; +$fa-var-video-slash: \f4e2; +$fa-var-battery-quarter: \f243; +$fa-var-battery-2: \f243; +$fa-var-radio: \f8d7; +$fa-var-baby-carriage: \f77d; +$fa-var-carriage-baby: \f77d; +$fa-var-traffic-light: \f637; +$fa-var-thermometer: \f491; +$fa-var-vr-cardboard: \f729; +$fa-var-hand-middle-finger: \f806; +$fa-var-percent: \25; +$fa-var-percentage: \25; +$fa-var-truck-moving: \f4df; +$fa-var-glass-water-droplet: \e4f5; +$fa-var-display: \e163; +$fa-var-face-smile: \f118; +$fa-var-smile: \f118; +$fa-var-thumbtack: \f08d; +$fa-var-thumb-tack: \f08d; +$fa-var-trophy: \f091; +$fa-var-person-praying: \f683; +$fa-var-pray: \f683; +$fa-var-hammer: \f6e3; +$fa-var-hand-peace: \f25b; +$fa-var-rotate: \f2f1; +$fa-var-sync-alt: \f2f1; +$fa-var-spinner: \f110; +$fa-var-robot: \f544; +$fa-var-peace: \f67c; +$fa-var-gears: \f085; +$fa-var-cogs: \f085; +$fa-var-warehouse: \f494; +$fa-var-arrow-up-right-dots: \e4b7; +$fa-var-splotch: \f5bc; +$fa-var-face-grin-hearts: \f584; +$fa-var-grin-hearts: \f584; +$fa-var-dice-four: \f524; +$fa-var-sim-card: \f7c4; +$fa-var-transgender: \f225; +$fa-var-transgender-alt: \f225; +$fa-var-mercury: \f223; +$fa-var-arrow-turn-down: \f149; +$fa-var-level-down: \f149; +$fa-var-person-falling-burst: \e547; +$fa-var-award: \f559; +$fa-var-ticket-simple: \f3ff; +$fa-var-ticket-alt: \f3ff; +$fa-var-building: \f1ad; +$fa-var-angles-left: \f100; +$fa-var-angle-double-left: \f100; +$fa-var-qrcode: \f029; +$fa-var-clock-rotate-left: \f1da; +$fa-var-history: \f1da; +$fa-var-face-grin-beam-sweat: \f583; +$fa-var-grin-beam-sweat: \f583; +$fa-var-file-export: \f56e; +$fa-var-arrow-right-from-file: \f56e; +$fa-var-shield: \f132; +$fa-var-shield-blank: \f132; +$fa-var-arrow-up-short-wide: \f885; +$fa-var-sort-amount-up-alt: \f885; +$fa-var-house-medical: \e3b2; +$fa-var-golf-ball-tee: \f450; +$fa-var-golf-ball: \f450; +$fa-var-circle-chevron-left: \f137; +$fa-var-chevron-circle-left: \f137; +$fa-var-house-chimney-window: \e00d; +$fa-var-pen-nib: \f5ad; +$fa-var-tent-arrow-turn-left: \e580; +$fa-var-tents: \e582; +$fa-var-wand-magic: \f0d0; +$fa-var-magic: \f0d0; +$fa-var-dog: \f6d3; +$fa-var-carrot: \f787; +$fa-var-moon: \f186; +$fa-var-wine-glass-empty: \f5ce; +$fa-var-wine-glass-alt: \f5ce; +$fa-var-cheese: \f7ef; +$fa-var-yin-yang: \f6ad; +$fa-var-music: \f001; +$fa-var-code-commit: \f386; +$fa-var-temperature-low: \f76b; +$fa-var-person-biking: \f84a; +$fa-var-biking: \f84a; +$fa-var-broom: \f51a; +$fa-var-shield-heart: \e574; +$fa-var-gopuram: \f664; +$fa-var-earth-oceania: \e47b; +$fa-var-globe-oceania: \e47b; +$fa-var-square-xmark: \f2d3; +$fa-var-times-square: \f2d3; +$fa-var-xmark-square: \f2d3; +$fa-var-hashtag: \23; +$fa-var-up-right-and-down-left-from-center: \f424; +$fa-var-expand-alt: \f424; +$fa-var-oil-can: \f613; +$fa-var-t: \54; +$fa-var-hippo: \f6ed; +$fa-var-chart-column: \e0e3; +$fa-var-infinity: \f534; +$fa-var-vial-circle-check: \e596; +$fa-var-person-arrow-down-to-line: \e538; +$fa-var-voicemail: \f897; +$fa-var-fan: \f863; +$fa-var-person-walking-luggage: \e554; +$fa-var-up-down: \f338; +$fa-var-arrows-alt-v: \f338; +$fa-var-cloud-moon-rain: \f73c; +$fa-var-calendar: \f133; +$fa-var-trailer: \e041; +$fa-var-bahai: \f666; +$fa-var-haykal: \f666; +$fa-var-sd-card: \f7c2; +$fa-var-dragon: \f6d5; +$fa-var-shoe-prints: \f54b; +$fa-var-circle-plus: \f055; +$fa-var-plus-circle: \f055; +$fa-var-face-grin-tongue-wink: \f58b; +$fa-var-grin-tongue-wink: \f58b; +$fa-var-hand-holding: \f4bd; +$fa-var-plug-circle-exclamation: \e55d; +$fa-var-link-slash: \f127; +$fa-var-chain-broken: \f127; +$fa-var-chain-slash: \f127; +$fa-var-unlink: \f127; +$fa-var-clone: \f24d; +$fa-var-person-walking-arrow-loop-left: \e551; +$fa-var-arrow-up-z-a: \f882; +$fa-var-sort-alpha-up-alt: \f882; +$fa-var-fire-flame-curved: \f7e4; +$fa-var-fire-alt: \f7e4; +$fa-var-tornado: \f76f; +$fa-var-file-circle-plus: \e494; +$fa-var-book-quran: \f687; +$fa-var-quran: \f687; +$fa-var-anchor: \f13d; +$fa-var-border-all: \f84c; +$fa-var-face-angry: \f556; +$fa-var-angry: \f556; +$fa-var-cookie-bite: \f564; +$fa-var-arrow-trend-down: \e097; +$fa-var-rss: \f09e; +$fa-var-feed: \f09e; +$fa-var-draw-polygon: \f5ee; +$fa-var-scale-balanced: \f24e; +$fa-var-balance-scale: \f24e; +$fa-var-gauge-simple-high: \f62a; +$fa-var-tachometer: \f62a; +$fa-var-tachometer-fast: \f62a; +$fa-var-shower: \f2cc; +$fa-var-desktop: \f390; +$fa-var-desktop-alt: \f390; +$fa-var-m: \4d; +$fa-var-table-list: \f00b; +$fa-var-th-list: \f00b; +$fa-var-comment-sms: \f7cd; +$fa-var-sms: \f7cd; +$fa-var-book: \f02d; +$fa-var-user-plus: \f234; +$fa-var-check: \f00c; +$fa-var-battery-three-quarters: \f241; +$fa-var-battery-4: \f241; +$fa-var-house-circle-check: \e509; +$fa-var-angle-left: \f104; +$fa-var-diagram-successor: \e47a; +$fa-var-truck-arrow-right: \e58b; +$fa-var-arrows-split-up-and-left: \e4bc; +$fa-var-hand-fist: \f6de; +$fa-var-fist-raised: \f6de; +$fa-var-cloud-moon: \f6c3; +$fa-var-briefcase: \f0b1; +$fa-var-person-falling: \e546; +$fa-var-image-portrait: \f3e0; +$fa-var-portrait: \f3e0; +$fa-var-user-tag: \f507; +$fa-var-rug: \e569; +$fa-var-earth-europe: \f7a2; +$fa-var-globe-europe: \f7a2; +$fa-var-cart-flatbed-suitcase: \f59d; +$fa-var-luggage-cart: \f59d; +$fa-var-rectangle-xmark: \f410; +$fa-var-rectangle-times: \f410; +$fa-var-times-rectangle: \f410; +$fa-var-window-close: \f410; +$fa-var-baht-sign: \e0ac; +$fa-var-book-open: \f518; +$fa-var-book-journal-whills: \f66a; +$fa-var-journal-whills: \f66a; +$fa-var-handcuffs: \e4f8; +$fa-var-triangle-exclamation: \f071; +$fa-var-exclamation-triangle: \f071; +$fa-var-warning: \f071; +$fa-var-database: \f1c0; +$fa-var-share: \f064; +$fa-var-arrow-turn-right: \f064; +$fa-var-mail-forward: \f064; +$fa-var-bottle-droplet: \e4c4; +$fa-var-mask-face: \e1d7; +$fa-var-hill-rockslide: \e508; +$fa-var-right-left: \f362; +$fa-var-exchange-alt: \f362; +$fa-var-paper-plane: \f1d8; +$fa-var-road-circle-exclamation: \e565; +$fa-var-dungeon: \f6d9; +$fa-var-align-right: \f038; +$fa-var-money-bill-1-wave: \f53b; +$fa-var-money-bill-wave-alt: \f53b; +$fa-var-life-ring: \f1cd; +$fa-var-hands: \f2a7; +$fa-var-sign-language: \f2a7; +$fa-var-signing: \f2a7; +$fa-var-calendar-day: \f783; +$fa-var-water-ladder: \f5c5; +$fa-var-ladder-water: \f5c5; +$fa-var-swimming-pool: \f5c5; +$fa-var-arrows-up-down: \f07d; +$fa-var-arrows-v: \f07d; +$fa-var-face-grimace: \f57f; +$fa-var-grimace: \f57f; +$fa-var-wheelchair-move: \e2ce; +$fa-var-wheelchair-alt: \e2ce; +$fa-var-turn-down: \f3be; +$fa-var-level-down-alt: \f3be; +$fa-var-person-walking-arrow-right: \e552; +$fa-var-square-envelope: \f199; +$fa-var-envelope-square: \f199; +$fa-var-dice: \f522; +$fa-var-bowling-ball: \f436; +$fa-var-brain: \f5dc; +$fa-var-bandage: \f462; +$fa-var-band-aid: \f462; +$fa-var-calendar-minus: \f272; +$fa-var-circle-xmark: \f057; +$fa-var-times-circle: \f057; +$fa-var-xmark-circle: \f057; +$fa-var-gifts: \f79c; +$fa-var-hotel: \f594; +$fa-var-earth-asia: \f57e; +$fa-var-globe-asia: \f57e; +$fa-var-id-card-clip: \f47f; +$fa-var-id-card-alt: \f47f; +$fa-var-magnifying-glass-plus: \f00e; +$fa-var-search-plus: \f00e; +$fa-var-thumbs-up: \f164; +$fa-var-user-clock: \f4fd; +$fa-var-hand-dots: \f461; +$fa-var-allergies: \f461; +$fa-var-file-invoice: \f570; +$fa-var-window-minimize: \f2d1; +$fa-var-mug-saucer: \f0f4; +$fa-var-coffee: \f0f4; +$fa-var-brush: \f55d; +$fa-var-mask: \f6fa; +$fa-var-magnifying-glass-minus: \f010; +$fa-var-search-minus: \f010; +$fa-var-ruler-vertical: \f548; +$fa-var-user-large: \f406; +$fa-var-user-alt: \f406; +$fa-var-train-tram: \e5b4; +$fa-var-user-nurse: \f82f; +$fa-var-syringe: \f48e; +$fa-var-cloud-sun: \f6c4; +$fa-var-stopwatch-20: \e06f; +$fa-var-square-full: \f45c; +$fa-var-magnet: \f076; +$fa-var-jar: \e516; +$fa-var-note-sticky: \f249; +$fa-var-sticky-note: \f249; +$fa-var-bug-slash: \e490; +$fa-var-arrow-up-from-water-pump: \e4b6; +$fa-var-bone: \f5d7; +$fa-var-user-injured: \f728; +$fa-var-face-sad-tear: \f5b4; +$fa-var-sad-tear: \f5b4; +$fa-var-plane: \f072; +$fa-var-tent-arrows-down: \e581; +$fa-var-exclamation: \21; +$fa-var-arrows-spin: \e4bb; +$fa-var-print: \f02f; +$fa-var-turkish-lira-sign: \e2bb; +$fa-var-try: \e2bb; +$fa-var-turkish-lira: \e2bb; +$fa-var-dollar-sign: \24; +$fa-var-dollar: \24; +$fa-var-usd: \24; +$fa-var-x: \58; +$fa-var-magnifying-glass-dollar: \f688; +$fa-var-search-dollar: \f688; +$fa-var-users-gear: \f509; +$fa-var-users-cog: \f509; +$fa-var-person-military-pointing: \e54a; +$fa-var-building-columns: \f19c; +$fa-var-bank: \f19c; +$fa-var-institution: \f19c; +$fa-var-museum: \f19c; +$fa-var-university: \f19c; +$fa-var-umbrella: \f0e9; +$fa-var-trowel: \e589; +$fa-var-d: \44; +$fa-var-stapler: \e5af; +$fa-var-masks-theater: \f630; +$fa-var-theater-masks: \f630; +$fa-var-kip-sign: \e1c4; +$fa-var-hand-point-left: \f0a5; +$fa-var-handshake-simple: \f4c6; +$fa-var-handshake-alt: \f4c6; +$fa-var-jet-fighter: \f0fb; +$fa-var-fighter-jet: \f0fb; +$fa-var-square-share-nodes: \f1e1; +$fa-var-share-alt-square: \f1e1; +$fa-var-barcode: \f02a; +$fa-var-plus-minus: \e43c; +$fa-var-video: \f03d; +$fa-var-video-camera: \f03d; +$fa-var-graduation-cap: \f19d; +$fa-var-mortar-board: \f19d; +$fa-var-hand-holding-medical: \e05c; +$fa-var-person-circle-check: \e53e; +$fa-var-turn-up: \f3bf; +$fa-var-level-up-alt: \f3bf; + +$fa-var-monero: \f3d0; +$fa-var-hooli: \f427; +$fa-var-yelp: \f1e9; +$fa-var-cc-visa: \f1f0; +$fa-var-lastfm: \f202; +$fa-var-shopware: \f5b5; +$fa-var-creative-commons-nc: \f4e8; +$fa-var-aws: \f375; +$fa-var-redhat: \f7bc; +$fa-var-yoast: \f2b1; +$fa-var-cloudflare: \e07d; +$fa-var-ups: \f7e0; +$fa-var-wpexplorer: \f2de; +$fa-var-dyalog: \f399; +$fa-var-bity: \f37a; +$fa-var-stackpath: \f842; +$fa-var-buysellads: \f20d; +$fa-var-first-order: \f2b0; +$fa-var-modx: \f285; +$fa-var-guilded: \e07e; +$fa-var-vnv: \f40b; +$fa-var-square-js: \f3b9; +$fa-var-js-square: \f3b9; +$fa-var-microsoft: \f3ca; +$fa-var-qq: \f1d6; +$fa-var-orcid: \f8d2; +$fa-var-java: \f4e4; +$fa-var-invision: \f7b0; +$fa-var-creative-commons-pd-alt: \f4ed; +$fa-var-centercode: \f380; +$fa-var-glide-g: \f2a6; +$fa-var-drupal: \f1a9; +$fa-var-hire-a-helper: \f3b0; +$fa-var-creative-commons-by: \f4e7; +$fa-var-unity: \e049; +$fa-var-whmcs: \f40d; +$fa-var-rocketchat: \f3e8; +$fa-var-vk: \f189; +$fa-var-untappd: \f405; +$fa-var-mailchimp: \f59e; +$fa-var-css3-alt: \f38b; +$fa-var-square-reddit: \f1a2; +$fa-var-reddit-square: \f1a2; +$fa-var-vimeo-v: \f27d; +$fa-var-contao: \f26d; +$fa-var-square-font-awesome: \e5ad; +$fa-var-deskpro: \f38f; +$fa-var-sistrix: \f3ee; +$fa-var-square-instagram: \e055; +$fa-var-instagram-square: \e055; +$fa-var-battle-net: \f835; +$fa-var-the-red-yeti: \f69d; +$fa-var-square-hacker-news: \f3af; +$fa-var-hacker-news-square: \f3af; +$fa-var-edge: \f282; +$fa-var-napster: \f3d2; +$fa-var-square-snapchat: \f2ad; +$fa-var-snapchat-square: \f2ad; +$fa-var-google-plus-g: \f0d5; +$fa-var-artstation: \f77a; +$fa-var-markdown: \f60f; +$fa-var-sourcetree: \f7d3; +$fa-var-google-plus: \f2b3; +$fa-var-diaspora: \f791; +$fa-var-foursquare: \f180; +$fa-var-stack-overflow: \f16c; +$fa-var-github-alt: \f113; +$fa-var-phoenix-squadron: \f511; +$fa-var-pagelines: \f18c; +$fa-var-algolia: \f36c; +$fa-var-red-river: \f3e3; +$fa-var-creative-commons-sa: \f4ef; +$fa-var-safari: \f267; +$fa-var-google: \f1a0; +$fa-var-square-font-awesome-stroke: \f35c; +$fa-var-font-awesome-alt: \f35c; +$fa-var-atlassian: \f77b; +$fa-var-linkedin-in: \f0e1; +$fa-var-digital-ocean: \f391; +$fa-var-nimblr: \f5a8; +$fa-var-chromecast: \f838; +$fa-var-evernote: \f839; +$fa-var-hacker-news: \f1d4; +$fa-var-creative-commons-sampling: \f4f0; +$fa-var-adversal: \f36a; +$fa-var-creative-commons: \f25e; +$fa-var-watchman-monitoring: \e087; +$fa-var-fonticons: \f280; +$fa-var-weixin: \f1d7; +$fa-var-shirtsinbulk: \f214; +$fa-var-codepen: \f1cb; +$fa-var-git-alt: \f841; +$fa-var-lyft: \f3c3; +$fa-var-rev: \f5b2; +$fa-var-windows: \f17a; +$fa-var-wizards-of-the-coast: \f730; +$fa-var-square-viadeo: \f2aa; +$fa-var-viadeo-square: \f2aa; +$fa-var-meetup: \f2e0; +$fa-var-centos: \f789; +$fa-var-adn: \f170; +$fa-var-cloudsmith: \f384; +$fa-var-pied-piper-alt: \f1a8; +$fa-var-square-dribbble: \f397; +$fa-var-dribbble-square: \f397; +$fa-var-codiepie: \f284; +$fa-var-node: \f419; +$fa-var-mix: \f3cb; +$fa-var-steam: \f1b6; +$fa-var-cc-apple-pay: \f416; +$fa-var-scribd: \f28a; +$fa-var-openid: \f19b; +$fa-var-instalod: \e081; +$fa-var-expeditedssl: \f23e; +$fa-var-sellcast: \f2da; +$fa-var-square-twitter: \f081; +$fa-var-twitter-square: \f081; +$fa-var-r-project: \f4f7; +$fa-var-delicious: \f1a5; +$fa-var-freebsd: \f3a4; +$fa-var-vuejs: \f41f; +$fa-var-accusoft: \f369; +$fa-var-ioxhost: \f208; +$fa-var-fonticons-fi: \f3a2; +$fa-var-app-store: \f36f; +$fa-var-cc-mastercard: \f1f1; +$fa-var-itunes-note: \f3b5; +$fa-var-golang: \e40f; +$fa-var-kickstarter: \f3bb; +$fa-var-grav: \f2d6; +$fa-var-weibo: \f18a; +$fa-var-uncharted: \e084; +$fa-var-firstdraft: \f3a1; +$fa-var-square-youtube: \f431; +$fa-var-youtube-square: \f431; +$fa-var-wikipedia-w: \f266; +$fa-var-wpressr: \f3e4; +$fa-var-rendact: \f3e4; +$fa-var-angellist: \f209; +$fa-var-galactic-republic: \f50c; +$fa-var-nfc-directional: \e530; +$fa-var-skype: \f17e; +$fa-var-joget: \f3b7; +$fa-var-fedora: \f798; +$fa-var-stripe-s: \f42a; +$fa-var-meta: \e49b; +$fa-var-laravel: \f3bd; +$fa-var-hotjar: \f3b1; +$fa-var-bluetooth-b: \f294; +$fa-var-sticker-mule: \f3f7; +$fa-var-creative-commons-zero: \f4f3; +$fa-var-hips: \f452; +$fa-var-behance: \f1b4; +$fa-var-reddit: \f1a1; +$fa-var-discord: \f392; +$fa-var-chrome: \f268; +$fa-var-app-store-ios: \f370; +$fa-var-cc-discover: \f1f2; +$fa-var-wpbeginner: \f297; +$fa-var-confluence: \f78d; +$fa-var-mdb: \f8ca; +$fa-var-dochub: \f394; +$fa-var-accessible-icon: \f368; +$fa-var-ebay: \f4f4; +$fa-var-amazon: \f270; +$fa-var-unsplash: \e07c; +$fa-var-yarn: \f7e3; +$fa-var-square-steam: \f1b7; +$fa-var-steam-square: \f1b7; +$fa-var-500px: \f26e; +$fa-var-square-vimeo: \f194; +$fa-var-vimeo-square: \f194; +$fa-var-asymmetrik: \f372; +$fa-var-font-awesome: \f2b4; +$fa-var-font-awesome-flag: \f2b4; +$fa-var-font-awesome-logo-full: \f2b4; +$fa-var-gratipay: \f184; +$fa-var-apple: \f179; +$fa-var-hive: \e07f; +$fa-var-gitkraken: \f3a6; +$fa-var-keybase: \f4f5; +$fa-var-apple-pay: \f415; +$fa-var-padlet: \e4a0; +$fa-var-amazon-pay: \f42c; +$fa-var-square-github: \f092; +$fa-var-github-square: \f092; +$fa-var-stumbleupon: \f1a4; +$fa-var-fedex: \f797; +$fa-var-phoenix-framework: \f3dc; +$fa-var-shopify: \e057; +$fa-var-neos: \f612; +$fa-var-hackerrank: \f5f7; +$fa-var-researchgate: \f4f8; +$fa-var-swift: \f8e1; +$fa-var-angular: \f420; +$fa-var-speakap: \f3f3; +$fa-var-angrycreative: \f36e; +$fa-var-y-combinator: \f23b; +$fa-var-empire: \f1d1; +$fa-var-envira: \f299; +$fa-var-square-gitlab: \e5ae; +$fa-var-gitlab-square: \e5ae; +$fa-var-studiovinari: \f3f8; +$fa-var-pied-piper: \f2ae; +$fa-var-wordpress: \f19a; +$fa-var-product-hunt: \f288; +$fa-var-firefox: \f269; +$fa-var-linode: \f2b8; +$fa-var-goodreads: \f3a8; +$fa-var-square-odnoklassniki: \f264; +$fa-var-odnoklassniki-square: \f264; +$fa-var-jsfiddle: \f1cc; +$fa-var-sith: \f512; +$fa-var-themeisle: \f2b2; +$fa-var-page4: \f3d7; +$fa-var-hashnode: \e499; +$fa-var-react: \f41b; +$fa-var-cc-paypal: \f1f4; +$fa-var-squarespace: \f5be; +$fa-var-cc-stripe: \f1f5; +$fa-var-creative-commons-share: \f4f2; +$fa-var-bitcoin: \f379; +$fa-var-keycdn: \f3ba; +$fa-var-opera: \f26a; +$fa-var-itch-io: \f83a; +$fa-var-umbraco: \f8e8; +$fa-var-galactic-senate: \f50d; +$fa-var-ubuntu: \f7df; +$fa-var-draft2digital: \f396; +$fa-var-stripe: \f429; +$fa-var-houzz: \f27c; +$fa-var-gg: \f260; +$fa-var-dhl: \f790; +$fa-var-square-pinterest: \f0d3; +$fa-var-pinterest-square: \f0d3; +$fa-var-xing: \f168; +$fa-var-blackberry: \f37b; +$fa-var-creative-commons-pd: \f4ec; +$fa-var-playstation: \f3df; +$fa-var-quinscape: \f459; +$fa-var-less: \f41d; +$fa-var-blogger-b: \f37d; +$fa-var-opencart: \f23d; +$fa-var-vine: \f1ca; +$fa-var-paypal: \f1ed; +$fa-var-gitlab: \f296; +$fa-var-typo3: \f42b; +$fa-var-reddit-alien: \f281; +$fa-var-yahoo: \f19e; +$fa-var-dailymotion: \e052; +$fa-var-affiliatetheme: \f36b; +$fa-var-pied-piper-pp: \f1a7; +$fa-var-bootstrap: \f836; +$fa-var-odnoklassniki: \f263; +$fa-var-nfc-symbol: \e531; +$fa-var-ethereum: \f42e; +$fa-var-speaker-deck: \f83c; +$fa-var-creative-commons-nc-eu: \f4e9; +$fa-var-patreon: \f3d9; +$fa-var-avianex: \f374; +$fa-var-ello: \f5f1; +$fa-var-gofore: \f3a7; +$fa-var-bimobject: \f378; +$fa-var-facebook-f: \f39e; +$fa-var-square-google-plus: \f0d4; +$fa-var-google-plus-square: \f0d4; +$fa-var-mandalorian: \f50f; +$fa-var-first-order-alt: \f50a; +$fa-var-osi: \f41a; +$fa-var-google-wallet: \f1ee; +$fa-var-d-and-d-beyond: \f6ca; +$fa-var-periscope: \f3da; +$fa-var-fulcrum: \f50b; +$fa-var-cloudscale: \f383; +$fa-var-forumbee: \f211; +$fa-var-mizuni: \f3cc; +$fa-var-schlix: \f3ea; +$fa-var-square-xing: \f169; +$fa-var-xing-square: \f169; +$fa-var-bandcamp: \f2d5; +$fa-var-wpforms: \f298; +$fa-var-cloudversify: \f385; +$fa-var-usps: \f7e1; +$fa-var-megaport: \f5a3; +$fa-var-magento: \f3c4; +$fa-var-spotify: \f1bc; +$fa-var-optin-monster: \f23c; +$fa-var-fly: \f417; +$fa-var-aviato: \f421; +$fa-var-itunes: \f3b4; +$fa-var-cuttlefish: \f38c; +$fa-var-blogger: \f37c; +$fa-var-flickr: \f16e; +$fa-var-viber: \f409; +$fa-var-soundcloud: \f1be; +$fa-var-digg: \f1a6; +$fa-var-tencent-weibo: \f1d5; +$fa-var-symfony: \f83d; +$fa-var-maxcdn: \f136; +$fa-var-etsy: \f2d7; +$fa-var-facebook-messenger: \f39f; +$fa-var-audible: \f373; +$fa-var-think-peaks: \f731; +$fa-var-bilibili: \e3d9; +$fa-var-erlang: \f39d; +$fa-var-cotton-bureau: \f89e; +$fa-var-dashcube: \f210; +$fa-var-42-group: \e080; +$fa-var-innosoft: \e080; +$fa-var-stack-exchange: \f18d; +$fa-var-elementor: \f430; +$fa-var-square-pied-piper: \e01e; +$fa-var-pied-piper-square: \e01e; +$fa-var-creative-commons-nd: \f4eb; +$fa-var-palfed: \f3d8; +$fa-var-superpowers: \f2dd; +$fa-var-resolving: \f3e7; +$fa-var-xbox: \f412; +$fa-var-searchengin: \f3eb; +$fa-var-tiktok: \e07b; +$fa-var-square-facebook: \f082; +$fa-var-facebook-square: \f082; +$fa-var-renren: \f18b; +$fa-var-linux: \f17c; +$fa-var-glide: \f2a5; +$fa-var-linkedin: \f08c; +$fa-var-hubspot: \f3b2; +$fa-var-deploydog: \f38e; +$fa-var-twitch: \f1e8; +$fa-var-ravelry: \f2d9; +$fa-var-mixer: \e056; +$fa-var-square-lastfm: \f203; +$fa-var-lastfm-square: \f203; +$fa-var-vimeo: \f40a; +$fa-var-mendeley: \f7b3; +$fa-var-uniregistry: \f404; +$fa-var-figma: \f799; +$fa-var-creative-commons-remix: \f4ee; +$fa-var-cc-amazon-pay: \f42d; +$fa-var-dropbox: \f16b; +$fa-var-instagram: \f16d; +$fa-var-cmplid: \e360; +$fa-var-facebook: \f09a; +$fa-var-gripfire: \f3ac; +$fa-var-jedi-order: \f50e; +$fa-var-uikit: \f403; +$fa-var-fort-awesome-alt: \f3a3; +$fa-var-phabricator: \f3db; +$fa-var-ussunnah: \f407; +$fa-var-earlybirds: \f39a; +$fa-var-trade-federation: \f513; +$fa-var-autoprefixer: \f41c; +$fa-var-whatsapp: \f232; +$fa-var-slideshare: \f1e7; +$fa-var-google-play: \f3ab; +$fa-var-viadeo: \f2a9; +$fa-var-line: \f3c0; +$fa-var-google-drive: \f3aa; +$fa-var-servicestack: \f3ec; +$fa-var-simplybuilt: \f215; +$fa-var-bitbucket: \f171; +$fa-var-imdb: \f2d8; +$fa-var-deezer: \e077; +$fa-var-raspberry-pi: \f7bb; +$fa-var-jira: \f7b1; +$fa-var-docker: \f395; +$fa-var-screenpal: \e570; +$fa-var-bluetooth: \f293; +$fa-var-gitter: \f426; +$fa-var-d-and-d: \f38d; +$fa-var-microblog: \e01a; +$fa-var-cc-diners-club: \f24c; +$fa-var-gg-circle: \f261; +$fa-var-pied-piper-hat: \f4e5; +$fa-var-kickstarter-k: \f3bc; +$fa-var-yandex: \f413; +$fa-var-readme: \f4d5; +$fa-var-html5: \f13b; +$fa-var-sellsy: \f213; +$fa-var-sass: \f41e; +$fa-var-wirsindhandwerk: \e2d0; +$fa-var-wsh: \e2d0; +$fa-var-buromobelexperte: \f37f; +$fa-var-salesforce: \f83b; +$fa-var-octopus-deploy: \e082; +$fa-var-medapps: \f3c6; +$fa-var-ns8: \f3d5; +$fa-var-pinterest-p: \f231; +$fa-var-apper: \f371; +$fa-var-fort-awesome: \f286; +$fa-var-waze: \f83f; +$fa-var-cc-jcb: \f24b; +$fa-var-snapchat: \f2ab; +$fa-var-snapchat-ghost: \f2ab; +$fa-var-fantasy-flight-games: \f6dc; +$fa-var-rust: \e07a; +$fa-var-wix: \f5cf; +$fa-var-square-behance: \f1b5; +$fa-var-behance-square: \f1b5; +$fa-var-supple: \f3f9; +$fa-var-rebel: \f1d0; +$fa-var-css3: \f13c; +$fa-var-staylinked: \f3f5; +$fa-var-kaggle: \f5fa; +$fa-var-space-awesome: \e5ac; +$fa-var-deviantart: \f1bd; +$fa-var-cpanel: \f388; +$fa-var-goodreads-g: \f3a9; +$fa-var-square-git: \f1d2; +$fa-var-git-square: \f1d2; +$fa-var-square-tumblr: \f174; +$fa-var-tumblr-square: \f174; +$fa-var-trello: \f181; +$fa-var-creative-commons-nc-jp: \f4ea; +$fa-var-get-pocket: \f265; +$fa-var-perbyte: \e083; +$fa-var-grunt: \f3ad; +$fa-var-weebly: \f5cc; +$fa-var-connectdevelop: \f20e; +$fa-var-leanpub: \f212; +$fa-var-black-tie: \f27e; +$fa-var-themeco: \f5c6; +$fa-var-python: \f3e2; +$fa-var-android: \f17b; +$fa-var-bots: \e340; +$fa-var-free-code-camp: \f2c5; +$fa-var-hornbill: \f592; +$fa-var-js: \f3b8; +$fa-var-ideal: \e013; +$fa-var-git: \f1d3; +$fa-var-dev: \f6cc; +$fa-var-sketch: \f7c6; +$fa-var-yandex-international: \f414; +$fa-var-cc-amex: \f1f3; +$fa-var-uber: \f402; +$fa-var-github: \f09b; +$fa-var-php: \f457; +$fa-var-alipay: \f642; +$fa-var-youtube: \f167; +$fa-var-skyatlas: \f216; +$fa-var-firefox-browser: \e007; +$fa-var-replyd: \f3e6; +$fa-var-suse: \f7d6; +$fa-var-jenkins: \f3b6; +$fa-var-twitter: \f099; +$fa-var-rockrms: \f3e9; +$fa-var-pinterest: \f0d2; +$fa-var-buffer: \f837; +$fa-var-npm: \f3d4; +$fa-var-yammer: \f840; +$fa-var-btc: \f15a; +$fa-var-dribbble: \f17d; +$fa-var-stumbleupon-circle: \f1a3; +$fa-var-internet-explorer: \f26b; +$fa-var-stubber: \e5c7; +$fa-var-telegram: \f2c6; +$fa-var-telegram-plane: \f2c6; +$fa-var-old-republic: \f510; +$fa-var-odysee: \e5c6; +$fa-var-square-whatsapp: \f40c; +$fa-var-whatsapp-square: \f40c; +$fa-var-node-js: \f3d3; +$fa-var-edge-legacy: \e078; +$fa-var-slack: \f198; +$fa-var-slack-hash: \f198; +$fa-var-medrt: \f3c8; +$fa-var-usb: \f287; +$fa-var-tumblr: \f173; +$fa-var-vaadin: \f408; +$fa-var-quora: \f2c4; +$fa-var-reacteurope: \f75d; +$fa-var-medium: \f23a; +$fa-var-medium-m: \f23a; +$fa-var-amilia: \f36d; +$fa-var-mixcloud: \f289; +$fa-var-flipboard: \f44d; +$fa-var-viacoin: \f237; +$fa-var-critical-role: \f6c9; +$fa-var-sitrox: \e44a; +$fa-var-discourse: \f393; +$fa-var-joomla: \f1aa; +$fa-var-mastodon: \f4f6; +$fa-var-airbnb: \f834; +$fa-var-wolf-pack-battalion: \f514; +$fa-var-buy-n-large: \f8a6; +$fa-var-gulp: \f3ae; +$fa-var-creative-commons-sampling-plus: \f4f1; +$fa-var-strava: \f428; +$fa-var-ember: \f423; +$fa-var-canadian-maple-leaf: \f785; +$fa-var-teamspeak: \f4f9; +$fa-var-pushed: \f3e1; +$fa-var-wordpress-simple: \f411; +$fa-var-nutritionix: \f3d6; +$fa-var-wodu: \e088; +$fa-var-google-pay: \e079; +$fa-var-intercom: \f7af; +$fa-var-zhihu: \f63f; +$fa-var-korvue: \f42f; +$fa-var-pix: \e43a; +$fa-var-steam-symbol: \f3f6; + +$fa-icons: ( + "0": $fa-var-0, + "1": $fa-var-1, + "2": $fa-var-2, + "3": $fa-var-3, + "4": $fa-var-4, + "5": $fa-var-5, + "6": $fa-var-6, + "7": $fa-var-7, + "8": $fa-var-8, + "9": $fa-var-9, + "fill-drip": $fa-var-fill-drip, + "arrows-to-circle": $fa-var-arrows-to-circle, + "circle-chevron-right": $fa-var-circle-chevron-right, + "chevron-circle-right": $fa-var-chevron-circle-right, + "at": $fa-var-at, + "trash-can": $fa-var-trash-can, + "trash-alt": $fa-var-trash-alt, + "text-height": $fa-var-text-height, + "user-xmark": $fa-var-user-xmark, + "user-times": $fa-var-user-times, + "stethoscope": $fa-var-stethoscope, + "message": $fa-var-message, + "comment-alt": $fa-var-comment-alt, + "info": $fa-var-info, + "down-left-and-up-right-to-center": $fa-var-down-left-and-up-right-to-center, + "compress-alt": $fa-var-compress-alt, + "explosion": $fa-var-explosion, + "file-lines": $fa-var-file-lines, + "file-alt": $fa-var-file-alt, + "file-text": $fa-var-file-text, + "wave-square": $fa-var-wave-square, + "ring": $fa-var-ring, + "building-un": $fa-var-building-un, + "dice-three": $fa-var-dice-three, + "calendar-days": $fa-var-calendar-days, + "calendar-alt": $fa-var-calendar-alt, + "anchor-circle-check": $fa-var-anchor-circle-check, + "building-circle-arrow-right": $fa-var-building-circle-arrow-right, + "volleyball": $fa-var-volleyball, + "volleyball-ball": $fa-var-volleyball-ball, + "arrows-up-to-line": $fa-var-arrows-up-to-line, + "sort-down": $fa-var-sort-down, + "sort-desc": $fa-var-sort-desc, + "circle-minus": $fa-var-circle-minus, + "minus-circle": $fa-var-minus-circle, + "door-open": $fa-var-door-open, + "right-from-bracket": $fa-var-right-from-bracket, + "sign-out-alt": $fa-var-sign-out-alt, + "atom": $fa-var-atom, + "soap": $fa-var-soap, + "icons": $fa-var-icons, + "heart-music-camera-bolt": $fa-var-heart-music-camera-bolt, + "microphone-lines-slash": $fa-var-microphone-lines-slash, + "microphone-alt-slash": $fa-var-microphone-alt-slash, + "bridge-circle-check": $fa-var-bridge-circle-check, + "pump-medical": $fa-var-pump-medical, + "fingerprint": $fa-var-fingerprint, + "hand-point-right": $fa-var-hand-point-right, + "magnifying-glass-location": $fa-var-magnifying-glass-location, + "search-location": $fa-var-search-location, + "forward-step": $fa-var-forward-step, + "step-forward": $fa-var-step-forward, + "face-smile-beam": $fa-var-face-smile-beam, + "smile-beam": $fa-var-smile-beam, + "flag-checkered": $fa-var-flag-checkered, + "football": $fa-var-football, + "football-ball": $fa-var-football-ball, + "school-circle-exclamation": $fa-var-school-circle-exclamation, + "crop": $fa-var-crop, + "angles-down": $fa-var-angles-down, + "angle-double-down": $fa-var-angle-double-down, + "users-rectangle": $fa-var-users-rectangle, + "people-roof": $fa-var-people-roof, + "people-line": $fa-var-people-line, + "beer-mug-empty": $fa-var-beer-mug-empty, + "beer": $fa-var-beer, + "diagram-predecessor": $fa-var-diagram-predecessor, + "arrow-up-long": $fa-var-arrow-up-long, + "long-arrow-up": $fa-var-long-arrow-up, + "fire-flame-simple": $fa-var-fire-flame-simple, + "burn": $fa-var-burn, + "person": $fa-var-person, + "male": $fa-var-male, + "laptop": $fa-var-laptop, + "file-csv": $fa-var-file-csv, + "menorah": $fa-var-menorah, + "truck-plane": $fa-var-truck-plane, + "record-vinyl": $fa-var-record-vinyl, + "face-grin-stars": $fa-var-face-grin-stars, + "grin-stars": $fa-var-grin-stars, + "bong": $fa-var-bong, + "spaghetti-monster-flying": $fa-var-spaghetti-monster-flying, + "pastafarianism": $fa-var-pastafarianism, + "arrow-down-up-across-line": $fa-var-arrow-down-up-across-line, + "spoon": $fa-var-spoon, + "utensil-spoon": $fa-var-utensil-spoon, + "jar-wheat": $fa-var-jar-wheat, + "envelopes-bulk": $fa-var-envelopes-bulk, + "mail-bulk": $fa-var-mail-bulk, + "file-circle-exclamation": $fa-var-file-circle-exclamation, + "circle-h": $fa-var-circle-h, + "hospital-symbol": $fa-var-hospital-symbol, + "pager": $fa-var-pager, + "address-book": $fa-var-address-book, + "contact-book": $fa-var-contact-book, + "strikethrough": $fa-var-strikethrough, + "k": $fa-var-k, + "landmark-flag": $fa-var-landmark-flag, + "pencil": $fa-var-pencil, + "pencil-alt": $fa-var-pencil-alt, + "backward": $fa-var-backward, + "caret-right": $fa-var-caret-right, + "comments": $fa-var-comments, + "paste": $fa-var-paste, + "file-clipboard": $fa-var-file-clipboard, + "code-pull-request": $fa-var-code-pull-request, + "clipboard-list": $fa-var-clipboard-list, + "truck-ramp-box": $fa-var-truck-ramp-box, + "truck-loading": $fa-var-truck-loading, + "user-check": $fa-var-user-check, + "vial-virus": $fa-var-vial-virus, + "sheet-plastic": $fa-var-sheet-plastic, + "blog": $fa-var-blog, + "user-ninja": $fa-var-user-ninja, + "person-arrow-up-from-line": $fa-var-person-arrow-up-from-line, + "scroll-torah": $fa-var-scroll-torah, + "torah": $fa-var-torah, + "broom-ball": $fa-var-broom-ball, + "quidditch": $fa-var-quidditch, + "quidditch-broom-ball": $fa-var-quidditch-broom-ball, + "toggle-off": $fa-var-toggle-off, + "box-archive": $fa-var-box-archive, + "archive": $fa-var-archive, + "person-drowning": $fa-var-person-drowning, + "arrow-down-9-1": $fa-var-arrow-down-9-1, + "sort-numeric-desc": $fa-var-sort-numeric-desc, + "sort-numeric-down-alt": $fa-var-sort-numeric-down-alt, + "face-grin-tongue-squint": $fa-var-face-grin-tongue-squint, + "grin-tongue-squint": $fa-var-grin-tongue-squint, + "spray-can": $fa-var-spray-can, + "truck-monster": $fa-var-truck-monster, + "w": $fa-var-w, + "earth-africa": $fa-var-earth-africa, + "globe-africa": $fa-var-globe-africa, + "rainbow": $fa-var-rainbow, + "circle-notch": $fa-var-circle-notch, + "tablet-screen-button": $fa-var-tablet-screen-button, + "tablet-alt": $fa-var-tablet-alt, + "paw": $fa-var-paw, + "cloud": $fa-var-cloud, + "trowel-bricks": $fa-var-trowel-bricks, + "face-flushed": $fa-var-face-flushed, + "flushed": $fa-var-flushed, + "hospital-user": $fa-var-hospital-user, + "tent-arrow-left-right": $fa-var-tent-arrow-left-right, + "gavel": $fa-var-gavel, + "legal": $fa-var-legal, + "binoculars": $fa-var-binoculars, + "microphone-slash": $fa-var-microphone-slash, + "box-tissue": $fa-var-box-tissue, + "motorcycle": $fa-var-motorcycle, + "bell-concierge": $fa-var-bell-concierge, + "concierge-bell": $fa-var-concierge-bell, + "pen-ruler": $fa-var-pen-ruler, + "pencil-ruler": $fa-var-pencil-ruler, + "people-arrows": $fa-var-people-arrows, + "people-arrows-left-right": $fa-var-people-arrows-left-right, + "mars-and-venus-burst": $fa-var-mars-and-venus-burst, + "square-caret-right": $fa-var-square-caret-right, + "caret-square-right": $fa-var-caret-square-right, + "scissors": $fa-var-scissors, + "cut": $fa-var-cut, + "sun-plant-wilt": $fa-var-sun-plant-wilt, + "toilets-portable": $fa-var-toilets-portable, + "hockey-puck": $fa-var-hockey-puck, + "table": $fa-var-table, + "magnifying-glass-arrow-right": $fa-var-magnifying-glass-arrow-right, + "tachograph-digital": $fa-var-tachograph-digital, + "digital-tachograph": $fa-var-digital-tachograph, + "users-slash": $fa-var-users-slash, + "clover": $fa-var-clover, + "reply": $fa-var-reply, + "mail-reply": $fa-var-mail-reply, + "star-and-crescent": $fa-var-star-and-crescent, + "house-fire": $fa-var-house-fire, + "square-minus": $fa-var-square-minus, + "minus-square": $fa-var-minus-square, + "helicopter": $fa-var-helicopter, + "compass": $fa-var-compass, + "square-caret-down": $fa-var-square-caret-down, + "caret-square-down": $fa-var-caret-square-down, + "file-circle-question": $fa-var-file-circle-question, + "laptop-code": $fa-var-laptop-code, + "swatchbook": $fa-var-swatchbook, + "prescription-bottle": $fa-var-prescription-bottle, + "bars": $fa-var-bars, + "navicon": $fa-var-navicon, + "people-group": $fa-var-people-group, + "hourglass-end": $fa-var-hourglass-end, + "hourglass-3": $fa-var-hourglass-3, + "heart-crack": $fa-var-heart-crack, + "heart-broken": $fa-var-heart-broken, + "square-up-right": $fa-var-square-up-right, + "external-link-square-alt": $fa-var-external-link-square-alt, + "face-kiss-beam": $fa-var-face-kiss-beam, + "kiss-beam": $fa-var-kiss-beam, + "film": $fa-var-film, + "ruler-horizontal": $fa-var-ruler-horizontal, + "people-robbery": $fa-var-people-robbery, + "lightbulb": $fa-var-lightbulb, + "caret-left": $fa-var-caret-left, + "circle-exclamation": $fa-var-circle-exclamation, + "exclamation-circle": $fa-var-exclamation-circle, + "school-circle-xmark": $fa-var-school-circle-xmark, + "arrow-right-from-bracket": $fa-var-arrow-right-from-bracket, + "sign-out": $fa-var-sign-out, + "circle-chevron-down": $fa-var-circle-chevron-down, + "chevron-circle-down": $fa-var-chevron-circle-down, + "unlock-keyhole": $fa-var-unlock-keyhole, + "unlock-alt": $fa-var-unlock-alt, + "cloud-showers-heavy": $fa-var-cloud-showers-heavy, + "headphones-simple": $fa-var-headphones-simple, + "headphones-alt": $fa-var-headphones-alt, + "sitemap": $fa-var-sitemap, + "circle-dollar-to-slot": $fa-var-circle-dollar-to-slot, + "donate": $fa-var-donate, + "memory": $fa-var-memory, + "road-spikes": $fa-var-road-spikes, + "fire-burner": $fa-var-fire-burner, + "flag": $fa-var-flag, + "hanukiah": $fa-var-hanukiah, + "feather": $fa-var-feather, + "volume-low": $fa-var-volume-low, + "volume-down": $fa-var-volume-down, + "comment-slash": $fa-var-comment-slash, + "cloud-sun-rain": $fa-var-cloud-sun-rain, + "compress": $fa-var-compress, + "wheat-awn": $fa-var-wheat-awn, + "wheat-alt": $fa-var-wheat-alt, + "ankh": $fa-var-ankh, + "hands-holding-child": $fa-var-hands-holding-child, + "asterisk": $fa-var-asterisk, + "square-check": $fa-var-square-check, + "check-square": $fa-var-check-square, + "peseta-sign": $fa-var-peseta-sign, + "heading": $fa-var-heading, + "header": $fa-var-header, + "ghost": $fa-var-ghost, + "list": $fa-var-list, + "list-squares": $fa-var-list-squares, + "square-phone-flip": $fa-var-square-phone-flip, + "phone-square-alt": $fa-var-phone-square-alt, + "cart-plus": $fa-var-cart-plus, + "gamepad": $fa-var-gamepad, + "circle-dot": $fa-var-circle-dot, + "dot-circle": $fa-var-dot-circle, + "face-dizzy": $fa-var-face-dizzy, + "dizzy": $fa-var-dizzy, + "egg": $fa-var-egg, + "house-medical-circle-xmark": $fa-var-house-medical-circle-xmark, + "campground": $fa-var-campground, + "folder-plus": $fa-var-folder-plus, + "futbol": $fa-var-futbol, + "futbol-ball": $fa-var-futbol-ball, + "soccer-ball": $fa-var-soccer-ball, + "paintbrush": $fa-var-paintbrush, + "paint-brush": $fa-var-paint-brush, + "lock": $fa-var-lock, + "gas-pump": $fa-var-gas-pump, + "hot-tub-person": $fa-var-hot-tub-person, + "hot-tub": $fa-var-hot-tub, + "map-location": $fa-var-map-location, + "map-marked": $fa-var-map-marked, + "house-flood-water": $fa-var-house-flood-water, + "tree": $fa-var-tree, + "bridge-lock": $fa-var-bridge-lock, + "sack-dollar": $fa-var-sack-dollar, + "pen-to-square": $fa-var-pen-to-square, + "edit": $fa-var-edit, + "car-side": $fa-var-car-side, + "share-nodes": $fa-var-share-nodes, + "share-alt": $fa-var-share-alt, + "heart-circle-minus": $fa-var-heart-circle-minus, + "hourglass-half": $fa-var-hourglass-half, + "hourglass-2": $fa-var-hourglass-2, + "microscope": $fa-var-microscope, + "sink": $fa-var-sink, + "bag-shopping": $fa-var-bag-shopping, + "shopping-bag": $fa-var-shopping-bag, + "arrow-down-z-a": $fa-var-arrow-down-z-a, + "sort-alpha-desc": $fa-var-sort-alpha-desc, + "sort-alpha-down-alt": $fa-var-sort-alpha-down-alt, + "mitten": $fa-var-mitten, + "person-rays": $fa-var-person-rays, + "users": $fa-var-users, + "eye-slash": $fa-var-eye-slash, + "flask-vial": $fa-var-flask-vial, + "hand": $fa-var-hand, + "hand-paper": $fa-var-hand-paper, + "om": $fa-var-om, + "worm": $fa-var-worm, + "house-circle-xmark": $fa-var-house-circle-xmark, + "plug": $fa-var-plug, + "chevron-up": $fa-var-chevron-up, + "hand-spock": $fa-var-hand-spock, + "stopwatch": $fa-var-stopwatch, + "face-kiss": $fa-var-face-kiss, + "kiss": $fa-var-kiss, + "bridge-circle-xmark": $fa-var-bridge-circle-xmark, + "face-grin-tongue": $fa-var-face-grin-tongue, + "grin-tongue": $fa-var-grin-tongue, + "chess-bishop": $fa-var-chess-bishop, + "face-grin-wink": $fa-var-face-grin-wink, + "grin-wink": $fa-var-grin-wink, + "ear-deaf": $fa-var-ear-deaf, + "deaf": $fa-var-deaf, + "deafness": $fa-var-deafness, + "hard-of-hearing": $fa-var-hard-of-hearing, + "road-circle-check": $fa-var-road-circle-check, + "dice-five": $fa-var-dice-five, + "square-rss": $fa-var-square-rss, + "rss-square": $fa-var-rss-square, + "land-mine-on": $fa-var-land-mine-on, + "i-cursor": $fa-var-i-cursor, + "stamp": $fa-var-stamp, + "stairs": $fa-var-stairs, + "i": $fa-var-i, + "hryvnia-sign": $fa-var-hryvnia-sign, + "hryvnia": $fa-var-hryvnia, + "pills": $fa-var-pills, + "face-grin-wide": $fa-var-face-grin-wide, + "grin-alt": $fa-var-grin-alt, + "tooth": $fa-var-tooth, + "v": $fa-var-v, + "bangladeshi-taka-sign": $fa-var-bangladeshi-taka-sign, + "bicycle": $fa-var-bicycle, + "staff-snake": $fa-var-staff-snake, + "rod-asclepius": $fa-var-rod-asclepius, + "rod-snake": $fa-var-rod-snake, + "staff-aesculapius": $fa-var-staff-aesculapius, + "head-side-cough-slash": $fa-var-head-side-cough-slash, + "truck-medical": $fa-var-truck-medical, + "ambulance": $fa-var-ambulance, + "wheat-awn-circle-exclamation": $fa-var-wheat-awn-circle-exclamation, + "snowman": $fa-var-snowman, + "mortar-pestle": $fa-var-mortar-pestle, + "road-barrier": $fa-var-road-barrier, + "school": $fa-var-school, + "igloo": $fa-var-igloo, + "joint": $fa-var-joint, + "angle-right": $fa-var-angle-right, + "horse": $fa-var-horse, + "q": $fa-var-q, + "g": $fa-var-g, + "notes-medical": $fa-var-notes-medical, + "temperature-half": $fa-var-temperature-half, + "temperature-2": $fa-var-temperature-2, + "thermometer-2": $fa-var-thermometer-2, + "thermometer-half": $fa-var-thermometer-half, + "dong-sign": $fa-var-dong-sign, + "capsules": $fa-var-capsules, + "poo-storm": $fa-var-poo-storm, + "poo-bolt": $fa-var-poo-bolt, + "face-frown-open": $fa-var-face-frown-open, + "frown-open": $fa-var-frown-open, + "hand-point-up": $fa-var-hand-point-up, + "money-bill": $fa-var-money-bill, + "bookmark": $fa-var-bookmark, + "align-justify": $fa-var-align-justify, + "umbrella-beach": $fa-var-umbrella-beach, + "helmet-un": $fa-var-helmet-un, + "bullseye": $fa-var-bullseye, + "bacon": $fa-var-bacon, + "hand-point-down": $fa-var-hand-point-down, + "arrow-up-from-bracket": $fa-var-arrow-up-from-bracket, + "folder": $fa-var-folder, + "folder-blank": $fa-var-folder-blank, + "file-waveform": $fa-var-file-waveform, + "file-medical-alt": $fa-var-file-medical-alt, + "radiation": $fa-var-radiation, + "chart-simple": $fa-var-chart-simple, + "mars-stroke": $fa-var-mars-stroke, + "vial": $fa-var-vial, + "gauge": $fa-var-gauge, + "dashboard": $fa-var-dashboard, + "gauge-med": $fa-var-gauge-med, + "tachometer-alt-average": $fa-var-tachometer-alt-average, + "wand-magic-sparkles": $fa-var-wand-magic-sparkles, + "magic-wand-sparkles": $fa-var-magic-wand-sparkles, + "e": $fa-var-e, + "pen-clip": $fa-var-pen-clip, + "pen-alt": $fa-var-pen-alt, + "bridge-circle-exclamation": $fa-var-bridge-circle-exclamation, + "user": $fa-var-user, + "school-circle-check": $fa-var-school-circle-check, + "dumpster": $fa-var-dumpster, + "van-shuttle": $fa-var-van-shuttle, + "shuttle-van": $fa-var-shuttle-van, + "building-user": $fa-var-building-user, + "square-caret-left": $fa-var-square-caret-left, + "caret-square-left": $fa-var-caret-square-left, + "highlighter": $fa-var-highlighter, + "key": $fa-var-key, + "bullhorn": $fa-var-bullhorn, + "globe": $fa-var-globe, + "synagogue": $fa-var-synagogue, + "person-half-dress": $fa-var-person-half-dress, + "road-bridge": $fa-var-road-bridge, + "location-arrow": $fa-var-location-arrow, + "c": $fa-var-c, + "tablet-button": $fa-var-tablet-button, + "building-lock": $fa-var-building-lock, + "pizza-slice": $fa-var-pizza-slice, + "money-bill-wave": $fa-var-money-bill-wave, + "chart-area": $fa-var-chart-area, + "area-chart": $fa-var-area-chart, + "house-flag": $fa-var-house-flag, + "person-circle-minus": $fa-var-person-circle-minus, + "ban": $fa-var-ban, + "cancel": $fa-var-cancel, + "camera-rotate": $fa-var-camera-rotate, + "spray-can-sparkles": $fa-var-spray-can-sparkles, + "air-freshener": $fa-var-air-freshener, + "star": $fa-var-star, + "repeat": $fa-var-repeat, + "cross": $fa-var-cross, + "box": $fa-var-box, + "venus-mars": $fa-var-venus-mars, + "arrow-pointer": $fa-var-arrow-pointer, + "mouse-pointer": $fa-var-mouse-pointer, + "maximize": $fa-var-maximize, + "expand-arrows-alt": $fa-var-expand-arrows-alt, + "charging-station": $fa-var-charging-station, + "shapes": $fa-var-shapes, + "triangle-circle-square": $fa-var-triangle-circle-square, + "shuffle": $fa-var-shuffle, + "random": $fa-var-random, + "person-running": $fa-var-person-running, + "running": $fa-var-running, + "mobile-retro": $fa-var-mobile-retro, + "grip-lines-vertical": $fa-var-grip-lines-vertical, + "spider": $fa-var-spider, + "hands-bound": $fa-var-hands-bound, + "file-invoice-dollar": $fa-var-file-invoice-dollar, + "plane-circle-exclamation": $fa-var-plane-circle-exclamation, + "x-ray": $fa-var-x-ray, + "spell-check": $fa-var-spell-check, + "slash": $fa-var-slash, + "computer-mouse": $fa-var-computer-mouse, + "mouse": $fa-var-mouse, + "arrow-right-to-bracket": $fa-var-arrow-right-to-bracket, + "sign-in": $fa-var-sign-in, + "shop-slash": $fa-var-shop-slash, + "store-alt-slash": $fa-var-store-alt-slash, + "server": $fa-var-server, + "virus-covid-slash": $fa-var-virus-covid-slash, + "shop-lock": $fa-var-shop-lock, + "hourglass-start": $fa-var-hourglass-start, + "hourglass-1": $fa-var-hourglass-1, + "blender-phone": $fa-var-blender-phone, + "building-wheat": $fa-var-building-wheat, + "person-breastfeeding": $fa-var-person-breastfeeding, + "right-to-bracket": $fa-var-right-to-bracket, + "sign-in-alt": $fa-var-sign-in-alt, + "venus": $fa-var-venus, + "passport": $fa-var-passport, + "heart-pulse": $fa-var-heart-pulse, + "heartbeat": $fa-var-heartbeat, + "people-carry-box": $fa-var-people-carry-box, + "people-carry": $fa-var-people-carry, + "temperature-high": $fa-var-temperature-high, + "microchip": $fa-var-microchip, + "crown": $fa-var-crown, + "weight-hanging": $fa-var-weight-hanging, + "xmarks-lines": $fa-var-xmarks-lines, + "file-prescription": $fa-var-file-prescription, + "weight-scale": $fa-var-weight-scale, + "weight": $fa-var-weight, + "user-group": $fa-var-user-group, + "user-friends": $fa-var-user-friends, + "arrow-up-a-z": $fa-var-arrow-up-a-z, + "sort-alpha-up": $fa-var-sort-alpha-up, + "chess-knight": $fa-var-chess-knight, + "face-laugh-squint": $fa-var-face-laugh-squint, + "laugh-squint": $fa-var-laugh-squint, + "wheelchair": $fa-var-wheelchair, + "circle-arrow-up": $fa-var-circle-arrow-up, + "arrow-circle-up": $fa-var-arrow-circle-up, + "toggle-on": $fa-var-toggle-on, + "person-walking": $fa-var-person-walking, + "walking": $fa-var-walking, + "l": $fa-var-l, + "fire": $fa-var-fire, + "bed-pulse": $fa-var-bed-pulse, + "procedures": $fa-var-procedures, + "shuttle-space": $fa-var-shuttle-space, + "space-shuttle": $fa-var-space-shuttle, + "face-laugh": $fa-var-face-laugh, + "laugh": $fa-var-laugh, + "folder-open": $fa-var-folder-open, + "heart-circle-plus": $fa-var-heart-circle-plus, + "code-fork": $fa-var-code-fork, + "city": $fa-var-city, + "microphone-lines": $fa-var-microphone-lines, + "microphone-alt": $fa-var-microphone-alt, + "pepper-hot": $fa-var-pepper-hot, + "unlock": $fa-var-unlock, + "colon-sign": $fa-var-colon-sign, + "headset": $fa-var-headset, + "store-slash": $fa-var-store-slash, + "road-circle-xmark": $fa-var-road-circle-xmark, + "user-minus": $fa-var-user-minus, + "mars-stroke-up": $fa-var-mars-stroke-up, + "mars-stroke-v": $fa-var-mars-stroke-v, + "champagne-glasses": $fa-var-champagne-glasses, + "glass-cheers": $fa-var-glass-cheers, + "clipboard": $fa-var-clipboard, + "house-circle-exclamation": $fa-var-house-circle-exclamation, + "file-arrow-up": $fa-var-file-arrow-up, + "file-upload": $fa-var-file-upload, + "wifi": $fa-var-wifi, + "wifi-3": $fa-var-wifi-3, + "wifi-strong": $fa-var-wifi-strong, + "bath": $fa-var-bath, + "bathtub": $fa-var-bathtub, + "underline": $fa-var-underline, + "user-pen": $fa-var-user-pen, + "user-edit": $fa-var-user-edit, + "signature": $fa-var-signature, + "stroopwafel": $fa-var-stroopwafel, + "bold": $fa-var-bold, + "anchor-lock": $fa-var-anchor-lock, + "building-ngo": $fa-var-building-ngo, + "manat-sign": $fa-var-manat-sign, + "not-equal": $fa-var-not-equal, + "border-top-left": $fa-var-border-top-left, + "border-style": $fa-var-border-style, + "map-location-dot": $fa-var-map-location-dot, + "map-marked-alt": $fa-var-map-marked-alt, + "jedi": $fa-var-jedi, + "square-poll-vertical": $fa-var-square-poll-vertical, + "poll": $fa-var-poll, + "mug-hot": $fa-var-mug-hot, + "car-battery": $fa-var-car-battery, + "battery-car": $fa-var-battery-car, + "gift": $fa-var-gift, + "dice-two": $fa-var-dice-two, + "chess-queen": $fa-var-chess-queen, + "glasses": $fa-var-glasses, + "chess-board": $fa-var-chess-board, + "building-circle-check": $fa-var-building-circle-check, + "person-chalkboard": $fa-var-person-chalkboard, + "mars-stroke-right": $fa-var-mars-stroke-right, + "mars-stroke-h": $fa-var-mars-stroke-h, + "hand-back-fist": $fa-var-hand-back-fist, + "hand-rock": $fa-var-hand-rock, + "square-caret-up": $fa-var-square-caret-up, + "caret-square-up": $fa-var-caret-square-up, + "cloud-showers-water": $fa-var-cloud-showers-water, + "chart-bar": $fa-var-chart-bar, + "bar-chart": $fa-var-bar-chart, + "hands-bubbles": $fa-var-hands-bubbles, + "hands-wash": $fa-var-hands-wash, + "less-than-equal": $fa-var-less-than-equal, + "train": $fa-var-train, + "eye-low-vision": $fa-var-eye-low-vision, + "low-vision": $fa-var-low-vision, + "crow": $fa-var-crow, + "sailboat": $fa-var-sailboat, + "window-restore": $fa-var-window-restore, + "square-plus": $fa-var-square-plus, + "plus-square": $fa-var-plus-square, + "torii-gate": $fa-var-torii-gate, + "frog": $fa-var-frog, + "bucket": $fa-var-bucket, + "image": $fa-var-image, + "microphone": $fa-var-microphone, + "cow": $fa-var-cow, + "caret-up": $fa-var-caret-up, + "screwdriver": $fa-var-screwdriver, + "folder-closed": $fa-var-folder-closed, + "house-tsunami": $fa-var-house-tsunami, + "square-nfi": $fa-var-square-nfi, + "arrow-up-from-ground-water": $fa-var-arrow-up-from-ground-water, + "martini-glass": $fa-var-martini-glass, + "glass-martini-alt": $fa-var-glass-martini-alt, + "rotate-left": $fa-var-rotate-left, + "rotate-back": $fa-var-rotate-back, + "rotate-backward": $fa-var-rotate-backward, + "undo-alt": $fa-var-undo-alt, + "table-columns": $fa-var-table-columns, + "columns": $fa-var-columns, + "lemon": $fa-var-lemon, + "head-side-mask": $fa-var-head-side-mask, + "handshake": $fa-var-handshake, + "gem": $fa-var-gem, + "dolly": $fa-var-dolly, + "dolly-box": $fa-var-dolly-box, + "smoking": $fa-var-smoking, + "minimize": $fa-var-minimize, + "compress-arrows-alt": $fa-var-compress-arrows-alt, + "monument": $fa-var-monument, + "snowplow": $fa-var-snowplow, + "angles-right": $fa-var-angles-right, + "angle-double-right": $fa-var-angle-double-right, + "cannabis": $fa-var-cannabis, + "circle-play": $fa-var-circle-play, + "play-circle": $fa-var-play-circle, + "tablets": $fa-var-tablets, + "ethernet": $fa-var-ethernet, + "euro-sign": $fa-var-euro-sign, + "eur": $fa-var-eur, + "euro": $fa-var-euro, + "chair": $fa-var-chair, + "circle-check": $fa-var-circle-check, + "check-circle": $fa-var-check-circle, + "circle-stop": $fa-var-circle-stop, + "stop-circle": $fa-var-stop-circle, + "compass-drafting": $fa-var-compass-drafting, + "drafting-compass": $fa-var-drafting-compass, + "plate-wheat": $fa-var-plate-wheat, + "icicles": $fa-var-icicles, + "person-shelter": $fa-var-person-shelter, + "neuter": $fa-var-neuter, + "id-badge": $fa-var-id-badge, + "marker": $fa-var-marker, + "face-laugh-beam": $fa-var-face-laugh-beam, + "laugh-beam": $fa-var-laugh-beam, + "helicopter-symbol": $fa-var-helicopter-symbol, + "universal-access": $fa-var-universal-access, + "circle-chevron-up": $fa-var-circle-chevron-up, + "chevron-circle-up": $fa-var-chevron-circle-up, + "lari-sign": $fa-var-lari-sign, + "volcano": $fa-var-volcano, + "person-walking-dashed-line-arrow-right": $fa-var-person-walking-dashed-line-arrow-right, + "sterling-sign": $fa-var-sterling-sign, + "gbp": $fa-var-gbp, + "pound-sign": $fa-var-pound-sign, + "viruses": $fa-var-viruses, + "square-person-confined": $fa-var-square-person-confined, + "user-tie": $fa-var-user-tie, + "arrow-down-long": $fa-var-arrow-down-long, + "long-arrow-down": $fa-var-long-arrow-down, + "tent-arrow-down-to-line": $fa-var-tent-arrow-down-to-line, + "certificate": $fa-var-certificate, + "reply-all": $fa-var-reply-all, + "mail-reply-all": $fa-var-mail-reply-all, + "suitcase": $fa-var-suitcase, + "person-skating": $fa-var-person-skating, + "skating": $fa-var-skating, + "filter-circle-dollar": $fa-var-filter-circle-dollar, + "funnel-dollar": $fa-var-funnel-dollar, + "camera-retro": $fa-var-camera-retro, + "circle-arrow-down": $fa-var-circle-arrow-down, + "arrow-circle-down": $fa-var-arrow-circle-down, + "file-import": $fa-var-file-import, + "arrow-right-to-file": $fa-var-arrow-right-to-file, + "square-arrow-up-right": $fa-var-square-arrow-up-right, + "external-link-square": $fa-var-external-link-square, + "box-open": $fa-var-box-open, + "scroll": $fa-var-scroll, + "spa": $fa-var-spa, + "location-pin-lock": $fa-var-location-pin-lock, + "pause": $fa-var-pause, + "hill-avalanche": $fa-var-hill-avalanche, + "temperature-empty": $fa-var-temperature-empty, + "temperature-0": $fa-var-temperature-0, + "thermometer-0": $fa-var-thermometer-0, + "thermometer-empty": $fa-var-thermometer-empty, + "bomb": $fa-var-bomb, + "registered": $fa-var-registered, + "address-card": $fa-var-address-card, + "contact-card": $fa-var-contact-card, + "vcard": $fa-var-vcard, + "scale-unbalanced-flip": $fa-var-scale-unbalanced-flip, + "balance-scale-right": $fa-var-balance-scale-right, + "subscript": $fa-var-subscript, + "diamond-turn-right": $fa-var-diamond-turn-right, + "directions": $fa-var-directions, + "burst": $fa-var-burst, + "house-laptop": $fa-var-house-laptop, + "laptop-house": $fa-var-laptop-house, + "face-tired": $fa-var-face-tired, + "tired": $fa-var-tired, + "money-bills": $fa-var-money-bills, + "smog": $fa-var-smog, + "crutch": $fa-var-crutch, + "cloud-arrow-up": $fa-var-cloud-arrow-up, + "cloud-upload": $fa-var-cloud-upload, + "cloud-upload-alt": $fa-var-cloud-upload-alt, + "palette": $fa-var-palette, + "arrows-turn-right": $fa-var-arrows-turn-right, + "vest": $fa-var-vest, + "ferry": $fa-var-ferry, + "arrows-down-to-people": $fa-var-arrows-down-to-people, + "seedling": $fa-var-seedling, + "sprout": $fa-var-sprout, + "left-right": $fa-var-left-right, + "arrows-alt-h": $fa-var-arrows-alt-h, + "boxes-packing": $fa-var-boxes-packing, + "circle-arrow-left": $fa-var-circle-arrow-left, + "arrow-circle-left": $fa-var-arrow-circle-left, + "group-arrows-rotate": $fa-var-group-arrows-rotate, + "bowl-food": $fa-var-bowl-food, + "candy-cane": $fa-var-candy-cane, + "arrow-down-wide-short": $fa-var-arrow-down-wide-short, + "sort-amount-asc": $fa-var-sort-amount-asc, + "sort-amount-down": $fa-var-sort-amount-down, + "cloud-bolt": $fa-var-cloud-bolt, + "thunderstorm": $fa-var-thunderstorm, + "text-slash": $fa-var-text-slash, + "remove-format": $fa-var-remove-format, + "face-smile-wink": $fa-var-face-smile-wink, + "smile-wink": $fa-var-smile-wink, + "file-word": $fa-var-file-word, + "file-powerpoint": $fa-var-file-powerpoint, + "arrows-left-right": $fa-var-arrows-left-right, + "arrows-h": $fa-var-arrows-h, + "house-lock": $fa-var-house-lock, + "cloud-arrow-down": $fa-var-cloud-arrow-down, + "cloud-download": $fa-var-cloud-download, + "cloud-download-alt": $fa-var-cloud-download-alt, + "children": $fa-var-children, + "chalkboard": $fa-var-chalkboard, + "blackboard": $fa-var-blackboard, + "user-large-slash": $fa-var-user-large-slash, + "user-alt-slash": $fa-var-user-alt-slash, + "envelope-open": $fa-var-envelope-open, + "handshake-simple-slash": $fa-var-handshake-simple-slash, + "handshake-alt-slash": $fa-var-handshake-alt-slash, + "mattress-pillow": $fa-var-mattress-pillow, + "guarani-sign": $fa-var-guarani-sign, + "arrows-rotate": $fa-var-arrows-rotate, + "refresh": $fa-var-refresh, + "sync": $fa-var-sync, + "fire-extinguisher": $fa-var-fire-extinguisher, + "cruzeiro-sign": $fa-var-cruzeiro-sign, + "greater-than-equal": $fa-var-greater-than-equal, + "shield-halved": $fa-var-shield-halved, + "shield-alt": $fa-var-shield-alt, + "book-atlas": $fa-var-book-atlas, + "atlas": $fa-var-atlas, + "virus": $fa-var-virus, + "envelope-circle-check": $fa-var-envelope-circle-check, + "layer-group": $fa-var-layer-group, + "arrows-to-dot": $fa-var-arrows-to-dot, + "archway": $fa-var-archway, + "heart-circle-check": $fa-var-heart-circle-check, + "house-chimney-crack": $fa-var-house-chimney-crack, + "house-damage": $fa-var-house-damage, + "file-zipper": $fa-var-file-zipper, + "file-archive": $fa-var-file-archive, + "square": $fa-var-square, + "martini-glass-empty": $fa-var-martini-glass-empty, + "glass-martini": $fa-var-glass-martini, + "couch": $fa-var-couch, + "cedi-sign": $fa-var-cedi-sign, + "italic": $fa-var-italic, + "church": $fa-var-church, + "comments-dollar": $fa-var-comments-dollar, + "democrat": $fa-var-democrat, + "z": $fa-var-z, + "person-skiing": $fa-var-person-skiing, + "skiing": $fa-var-skiing, + "road-lock": $fa-var-road-lock, + "a": $fa-var-a, + "temperature-arrow-down": $fa-var-temperature-arrow-down, + "temperature-down": $fa-var-temperature-down, + "feather-pointed": $fa-var-feather-pointed, + "feather-alt": $fa-var-feather-alt, + "p": $fa-var-p, + "snowflake": $fa-var-snowflake, + "newspaper": $fa-var-newspaper, + "rectangle-ad": $fa-var-rectangle-ad, + "ad": $fa-var-ad, + "circle-arrow-right": $fa-var-circle-arrow-right, + "arrow-circle-right": $fa-var-arrow-circle-right, + "filter-circle-xmark": $fa-var-filter-circle-xmark, + "locust": $fa-var-locust, + "sort": $fa-var-sort, + "unsorted": $fa-var-unsorted, + "list-ol": $fa-var-list-ol, + "list-1-2": $fa-var-list-1-2, + "list-numeric": $fa-var-list-numeric, + "person-dress-burst": $fa-var-person-dress-burst, + "money-check-dollar": $fa-var-money-check-dollar, + "money-check-alt": $fa-var-money-check-alt, + "vector-square": $fa-var-vector-square, + "bread-slice": $fa-var-bread-slice, + "language": $fa-var-language, + "face-kiss-wink-heart": $fa-var-face-kiss-wink-heart, + "kiss-wink-heart": $fa-var-kiss-wink-heart, + "filter": $fa-var-filter, + "question": $fa-var-question, + "file-signature": $fa-var-file-signature, + "up-down-left-right": $fa-var-up-down-left-right, + "arrows-alt": $fa-var-arrows-alt, + "house-chimney-user": $fa-var-house-chimney-user, + "hand-holding-heart": $fa-var-hand-holding-heart, + "puzzle-piece": $fa-var-puzzle-piece, + "money-check": $fa-var-money-check, + "star-half-stroke": $fa-var-star-half-stroke, + "star-half-alt": $fa-var-star-half-alt, + "code": $fa-var-code, + "whiskey-glass": $fa-var-whiskey-glass, + "glass-whiskey": $fa-var-glass-whiskey, + "building-circle-exclamation": $fa-var-building-circle-exclamation, + "magnifying-glass-chart": $fa-var-magnifying-glass-chart, + "arrow-up-right-from-square": $fa-var-arrow-up-right-from-square, + "external-link": $fa-var-external-link, + "cubes-stacked": $fa-var-cubes-stacked, + "won-sign": $fa-var-won-sign, + "krw": $fa-var-krw, + "won": $fa-var-won, + "virus-covid": $fa-var-virus-covid, + "austral-sign": $fa-var-austral-sign, + "f": $fa-var-f, + "leaf": $fa-var-leaf, + "road": $fa-var-road, + "taxi": $fa-var-taxi, + "cab": $fa-var-cab, + "person-circle-plus": $fa-var-person-circle-plus, + "chart-pie": $fa-var-chart-pie, + "pie-chart": $fa-var-pie-chart, + "bolt-lightning": $fa-var-bolt-lightning, + "sack-xmark": $fa-var-sack-xmark, + "file-excel": $fa-var-file-excel, + "file-contract": $fa-var-file-contract, + "fish-fins": $fa-var-fish-fins, + "building-flag": $fa-var-building-flag, + "face-grin-beam": $fa-var-face-grin-beam, + "grin-beam": $fa-var-grin-beam, + "object-ungroup": $fa-var-object-ungroup, + "poop": $fa-var-poop, + "location-pin": $fa-var-location-pin, + "map-marker": $fa-var-map-marker, + "kaaba": $fa-var-kaaba, + "toilet-paper": $fa-var-toilet-paper, + "helmet-safety": $fa-var-helmet-safety, + "hard-hat": $fa-var-hard-hat, + "hat-hard": $fa-var-hat-hard, + "eject": $fa-var-eject, + "circle-right": $fa-var-circle-right, + "arrow-alt-circle-right": $fa-var-arrow-alt-circle-right, + "plane-circle-check": $fa-var-plane-circle-check, + "face-rolling-eyes": $fa-var-face-rolling-eyes, + "meh-rolling-eyes": $fa-var-meh-rolling-eyes, + "object-group": $fa-var-object-group, + "chart-line": $fa-var-chart-line, + "line-chart": $fa-var-line-chart, + "mask-ventilator": $fa-var-mask-ventilator, + "arrow-right": $fa-var-arrow-right, + "signs-post": $fa-var-signs-post, + "map-signs": $fa-var-map-signs, + "cash-register": $fa-var-cash-register, + "person-circle-question": $fa-var-person-circle-question, + "h": $fa-var-h, + "tarp": $fa-var-tarp, + "screwdriver-wrench": $fa-var-screwdriver-wrench, + "tools": $fa-var-tools, + "arrows-to-eye": $fa-var-arrows-to-eye, + "plug-circle-bolt": $fa-var-plug-circle-bolt, + "heart": $fa-var-heart, + "mars-and-venus": $fa-var-mars-and-venus, + "house-user": $fa-var-house-user, + "home-user": $fa-var-home-user, + "dumpster-fire": $fa-var-dumpster-fire, + "house-crack": $fa-var-house-crack, + "martini-glass-citrus": $fa-var-martini-glass-citrus, + "cocktail": $fa-var-cocktail, + "face-surprise": $fa-var-face-surprise, + "surprise": $fa-var-surprise, + "bottle-water": $fa-var-bottle-water, + "circle-pause": $fa-var-circle-pause, + "pause-circle": $fa-var-pause-circle, + "toilet-paper-slash": $fa-var-toilet-paper-slash, + "apple-whole": $fa-var-apple-whole, + "apple-alt": $fa-var-apple-alt, + "kitchen-set": $fa-var-kitchen-set, + "r": $fa-var-r, + "temperature-quarter": $fa-var-temperature-quarter, + "temperature-1": $fa-var-temperature-1, + "thermometer-1": $fa-var-thermometer-1, + "thermometer-quarter": $fa-var-thermometer-quarter, + "cube": $fa-var-cube, + "bitcoin-sign": $fa-var-bitcoin-sign, + "shield-dog": $fa-var-shield-dog, + "solar-panel": $fa-var-solar-panel, + "lock-open": $fa-var-lock-open, + "elevator": $fa-var-elevator, + "money-bill-transfer": $fa-var-money-bill-transfer, + "money-bill-trend-up": $fa-var-money-bill-trend-up, + "house-flood-water-circle-arrow-right": $fa-var-house-flood-water-circle-arrow-right, + "square-poll-horizontal": $fa-var-square-poll-horizontal, + "poll-h": $fa-var-poll-h, + "circle": $fa-var-circle, + "backward-fast": $fa-var-backward-fast, + "fast-backward": $fa-var-fast-backward, + "recycle": $fa-var-recycle, + "user-astronaut": $fa-var-user-astronaut, + "plane-slash": $fa-var-plane-slash, + "trademark": $fa-var-trademark, + "basketball": $fa-var-basketball, + "basketball-ball": $fa-var-basketball-ball, + "satellite-dish": $fa-var-satellite-dish, + "circle-up": $fa-var-circle-up, + "arrow-alt-circle-up": $fa-var-arrow-alt-circle-up, + "mobile-screen-button": $fa-var-mobile-screen-button, + "mobile-alt": $fa-var-mobile-alt, + "volume-high": $fa-var-volume-high, + "volume-up": $fa-var-volume-up, + "users-rays": $fa-var-users-rays, + "wallet": $fa-var-wallet, + "clipboard-check": $fa-var-clipboard-check, + "file-audio": $fa-var-file-audio, + "burger": $fa-var-burger, + "hamburger": $fa-var-hamburger, + "wrench": $fa-var-wrench, + "bugs": $fa-var-bugs, + "rupee-sign": $fa-var-rupee-sign, + "rupee": $fa-var-rupee, + "file-image": $fa-var-file-image, + "circle-question": $fa-var-circle-question, + "question-circle": $fa-var-question-circle, + "plane-departure": $fa-var-plane-departure, + "handshake-slash": $fa-var-handshake-slash, + "book-bookmark": $fa-var-book-bookmark, + "code-branch": $fa-var-code-branch, + "hat-cowboy": $fa-var-hat-cowboy, + "bridge": $fa-var-bridge, + "phone-flip": $fa-var-phone-flip, + "phone-alt": $fa-var-phone-alt, + "truck-front": $fa-var-truck-front, + "cat": $fa-var-cat, + "anchor-circle-exclamation": $fa-var-anchor-circle-exclamation, + "truck-field": $fa-var-truck-field, + "route": $fa-var-route, + "clipboard-question": $fa-var-clipboard-question, + "panorama": $fa-var-panorama, + "comment-medical": $fa-var-comment-medical, + "teeth-open": $fa-var-teeth-open, + "file-circle-minus": $fa-var-file-circle-minus, + "tags": $fa-var-tags, + "wine-glass": $fa-var-wine-glass, + "forward-fast": $fa-var-forward-fast, + "fast-forward": $fa-var-fast-forward, + "face-meh-blank": $fa-var-face-meh-blank, + "meh-blank": $fa-var-meh-blank, + "square-parking": $fa-var-square-parking, + "parking": $fa-var-parking, + "house-signal": $fa-var-house-signal, + "bars-progress": $fa-var-bars-progress, + "tasks-alt": $fa-var-tasks-alt, + "faucet-drip": $fa-var-faucet-drip, + "cart-flatbed": $fa-var-cart-flatbed, + "dolly-flatbed": $fa-var-dolly-flatbed, + "ban-smoking": $fa-var-ban-smoking, + "smoking-ban": $fa-var-smoking-ban, + "terminal": $fa-var-terminal, + "mobile-button": $fa-var-mobile-button, + "house-medical-flag": $fa-var-house-medical-flag, + "basket-shopping": $fa-var-basket-shopping, + "shopping-basket": $fa-var-shopping-basket, + "tape": $fa-var-tape, + "bus-simple": $fa-var-bus-simple, + "bus-alt": $fa-var-bus-alt, + "eye": $fa-var-eye, + "face-sad-cry": $fa-var-face-sad-cry, + "sad-cry": $fa-var-sad-cry, + "audio-description": $fa-var-audio-description, + "person-military-to-person": $fa-var-person-military-to-person, + "file-shield": $fa-var-file-shield, + "user-slash": $fa-var-user-slash, + "pen": $fa-var-pen, + "tower-observation": $fa-var-tower-observation, + "file-code": $fa-var-file-code, + "signal": $fa-var-signal, + "signal-5": $fa-var-signal-5, + "signal-perfect": $fa-var-signal-perfect, + "bus": $fa-var-bus, + "heart-circle-xmark": $fa-var-heart-circle-xmark, + "house-chimney": $fa-var-house-chimney, + "home-lg": $fa-var-home-lg, + "window-maximize": $fa-var-window-maximize, + "face-frown": $fa-var-face-frown, + "frown": $fa-var-frown, + "prescription": $fa-var-prescription, + "shop": $fa-var-shop, + "store-alt": $fa-var-store-alt, + "floppy-disk": $fa-var-floppy-disk, + "save": $fa-var-save, + "vihara": $fa-var-vihara, + "scale-unbalanced": $fa-var-scale-unbalanced, + "balance-scale-left": $fa-var-balance-scale-left, + "sort-up": $fa-var-sort-up, + "sort-asc": $fa-var-sort-asc, + "comment-dots": $fa-var-comment-dots, + "commenting": $fa-var-commenting, + "plant-wilt": $fa-var-plant-wilt, + "diamond": $fa-var-diamond, + "face-grin-squint": $fa-var-face-grin-squint, + "grin-squint": $fa-var-grin-squint, + "hand-holding-dollar": $fa-var-hand-holding-dollar, + "hand-holding-usd": $fa-var-hand-holding-usd, + "bacterium": $fa-var-bacterium, + "hand-pointer": $fa-var-hand-pointer, + "drum-steelpan": $fa-var-drum-steelpan, + "hand-scissors": $fa-var-hand-scissors, + "hands-praying": $fa-var-hands-praying, + "praying-hands": $fa-var-praying-hands, + "arrow-rotate-right": $fa-var-arrow-rotate-right, + "arrow-right-rotate": $fa-var-arrow-right-rotate, + "arrow-rotate-forward": $fa-var-arrow-rotate-forward, + "redo": $fa-var-redo, + "biohazard": $fa-var-biohazard, + "location-crosshairs": $fa-var-location-crosshairs, + "location": $fa-var-location, + "mars-double": $fa-var-mars-double, + "child-dress": $fa-var-child-dress, + "users-between-lines": $fa-var-users-between-lines, + "lungs-virus": $fa-var-lungs-virus, + "face-grin-tears": $fa-var-face-grin-tears, + "grin-tears": $fa-var-grin-tears, + "phone": $fa-var-phone, + "calendar-xmark": $fa-var-calendar-xmark, + "calendar-times": $fa-var-calendar-times, + "child-reaching": $fa-var-child-reaching, + "head-side-virus": $fa-var-head-side-virus, + "user-gear": $fa-var-user-gear, + "user-cog": $fa-var-user-cog, + "arrow-up-1-9": $fa-var-arrow-up-1-9, + "sort-numeric-up": $fa-var-sort-numeric-up, + "door-closed": $fa-var-door-closed, + "shield-virus": $fa-var-shield-virus, + "dice-six": $fa-var-dice-six, + "mosquito-net": $fa-var-mosquito-net, + "bridge-water": $fa-var-bridge-water, + "person-booth": $fa-var-person-booth, + "text-width": $fa-var-text-width, + "hat-wizard": $fa-var-hat-wizard, + "pen-fancy": $fa-var-pen-fancy, + "person-digging": $fa-var-person-digging, + "digging": $fa-var-digging, + "trash": $fa-var-trash, + "gauge-simple": $fa-var-gauge-simple, + "gauge-simple-med": $fa-var-gauge-simple-med, + "tachometer-average": $fa-var-tachometer-average, + "book-medical": $fa-var-book-medical, + "poo": $fa-var-poo, + "quote-right": $fa-var-quote-right, + "quote-right-alt": $fa-var-quote-right-alt, + "shirt": $fa-var-shirt, + "t-shirt": $fa-var-t-shirt, + "tshirt": $fa-var-tshirt, + "cubes": $fa-var-cubes, + "divide": $fa-var-divide, + "tenge-sign": $fa-var-tenge-sign, + "tenge": $fa-var-tenge, + "headphones": $fa-var-headphones, + "hands-holding": $fa-var-hands-holding, + "hands-clapping": $fa-var-hands-clapping, + "republican": $fa-var-republican, + "arrow-left": $fa-var-arrow-left, + "person-circle-xmark": $fa-var-person-circle-xmark, + "ruler": $fa-var-ruler, + "align-left": $fa-var-align-left, + "dice-d6": $fa-var-dice-d6, + "restroom": $fa-var-restroom, + "j": $fa-var-j, + "users-viewfinder": $fa-var-users-viewfinder, + "file-video": $fa-var-file-video, + "up-right-from-square": $fa-var-up-right-from-square, + "external-link-alt": $fa-var-external-link-alt, + "table-cells": $fa-var-table-cells, + "th": $fa-var-th, + "file-pdf": $fa-var-file-pdf, + "book-bible": $fa-var-book-bible, + "bible": $fa-var-bible, + "o": $fa-var-o, + "suitcase-medical": $fa-var-suitcase-medical, + "medkit": $fa-var-medkit, + "user-secret": $fa-var-user-secret, + "otter": $fa-var-otter, + "person-dress": $fa-var-person-dress, + "female": $fa-var-female, + "comment-dollar": $fa-var-comment-dollar, + "business-time": $fa-var-business-time, + "briefcase-clock": $fa-var-briefcase-clock, + "table-cells-large": $fa-var-table-cells-large, + "th-large": $fa-var-th-large, + "book-tanakh": $fa-var-book-tanakh, + "tanakh": $fa-var-tanakh, + "phone-volume": $fa-var-phone-volume, + "volume-control-phone": $fa-var-volume-control-phone, + "hat-cowboy-side": $fa-var-hat-cowboy-side, + "clipboard-user": $fa-var-clipboard-user, + "child": $fa-var-child, + "lira-sign": $fa-var-lira-sign, + "satellite": $fa-var-satellite, + "plane-lock": $fa-var-plane-lock, + "tag": $fa-var-tag, + "comment": $fa-var-comment, + "cake-candles": $fa-var-cake-candles, + "birthday-cake": $fa-var-birthday-cake, + "cake": $fa-var-cake, + "envelope": $fa-var-envelope, + "angles-up": $fa-var-angles-up, + "angle-double-up": $fa-var-angle-double-up, + "paperclip": $fa-var-paperclip, + "arrow-right-to-city": $fa-var-arrow-right-to-city, + "ribbon": $fa-var-ribbon, + "lungs": $fa-var-lungs, + "arrow-up-9-1": $fa-var-arrow-up-9-1, + "sort-numeric-up-alt": $fa-var-sort-numeric-up-alt, + "litecoin-sign": $fa-var-litecoin-sign, + "border-none": $fa-var-border-none, + "circle-nodes": $fa-var-circle-nodes, + "parachute-box": $fa-var-parachute-box, + "indent": $fa-var-indent, + "truck-field-un": $fa-var-truck-field-un, + "hourglass": $fa-var-hourglass, + "hourglass-empty": $fa-var-hourglass-empty, + "mountain": $fa-var-mountain, + "user-doctor": $fa-var-user-doctor, + "user-md": $fa-var-user-md, + "circle-info": $fa-var-circle-info, + "info-circle": $fa-var-info-circle, + "cloud-meatball": $fa-var-cloud-meatball, + "camera": $fa-var-camera, + "camera-alt": $fa-var-camera-alt, + "square-virus": $fa-var-square-virus, + "meteor": $fa-var-meteor, + "car-on": $fa-var-car-on, + "sleigh": $fa-var-sleigh, + "arrow-down-1-9": $fa-var-arrow-down-1-9, + "sort-numeric-asc": $fa-var-sort-numeric-asc, + "sort-numeric-down": $fa-var-sort-numeric-down, + "hand-holding-droplet": $fa-var-hand-holding-droplet, + "hand-holding-water": $fa-var-hand-holding-water, + "water": $fa-var-water, + "calendar-check": $fa-var-calendar-check, + "braille": $fa-var-braille, + "prescription-bottle-medical": $fa-var-prescription-bottle-medical, + "prescription-bottle-alt": $fa-var-prescription-bottle-alt, + "landmark": $fa-var-landmark, + "truck": $fa-var-truck, + "crosshairs": $fa-var-crosshairs, + "person-cane": $fa-var-person-cane, + "tent": $fa-var-tent, + "vest-patches": $fa-var-vest-patches, + "check-double": $fa-var-check-double, + "arrow-down-a-z": $fa-var-arrow-down-a-z, + "sort-alpha-asc": $fa-var-sort-alpha-asc, + "sort-alpha-down": $fa-var-sort-alpha-down, + "money-bill-wheat": $fa-var-money-bill-wheat, + "cookie": $fa-var-cookie, + "arrow-rotate-left": $fa-var-arrow-rotate-left, + "arrow-left-rotate": $fa-var-arrow-left-rotate, + "arrow-rotate-back": $fa-var-arrow-rotate-back, + "arrow-rotate-backward": $fa-var-arrow-rotate-backward, + "undo": $fa-var-undo, + "hard-drive": $fa-var-hard-drive, + "hdd": $fa-var-hdd, + "face-grin-squint-tears": $fa-var-face-grin-squint-tears, + "grin-squint-tears": $fa-var-grin-squint-tears, + "dumbbell": $fa-var-dumbbell, + "rectangle-list": $fa-var-rectangle-list, + "list-alt": $fa-var-list-alt, + "tarp-droplet": $fa-var-tarp-droplet, + "house-medical-circle-check": $fa-var-house-medical-circle-check, + "person-skiing-nordic": $fa-var-person-skiing-nordic, + "skiing-nordic": $fa-var-skiing-nordic, + "calendar-plus": $fa-var-calendar-plus, + "plane-arrival": $fa-var-plane-arrival, + "circle-left": $fa-var-circle-left, + "arrow-alt-circle-left": $fa-var-arrow-alt-circle-left, + "train-subway": $fa-var-train-subway, + "subway": $fa-var-subway, + "chart-gantt": $fa-var-chart-gantt, + "indian-rupee-sign": $fa-var-indian-rupee-sign, + "indian-rupee": $fa-var-indian-rupee, + "inr": $fa-var-inr, + "crop-simple": $fa-var-crop-simple, + "crop-alt": $fa-var-crop-alt, + "money-bill-1": $fa-var-money-bill-1, + "money-bill-alt": $fa-var-money-bill-alt, + "left-long": $fa-var-left-long, + "long-arrow-alt-left": $fa-var-long-arrow-alt-left, + "dna": $fa-var-dna, + "virus-slash": $fa-var-virus-slash, + "minus": $fa-var-minus, + "subtract": $fa-var-subtract, + "chess": $fa-var-chess, + "arrow-left-long": $fa-var-arrow-left-long, + "long-arrow-left": $fa-var-long-arrow-left, + "plug-circle-check": $fa-var-plug-circle-check, + "street-view": $fa-var-street-view, + "franc-sign": $fa-var-franc-sign, + "volume-off": $fa-var-volume-off, + "hands-asl-interpreting": $fa-var-hands-asl-interpreting, + "american-sign-language-interpreting": $fa-var-american-sign-language-interpreting, + "asl-interpreting": $fa-var-asl-interpreting, + "hands-american-sign-language-interpreting": $fa-var-hands-american-sign-language-interpreting, + "gear": $fa-var-gear, + "cog": $fa-var-cog, + "droplet-slash": $fa-var-droplet-slash, + "tint-slash": $fa-var-tint-slash, + "mosque": $fa-var-mosque, + "mosquito": $fa-var-mosquito, + "star-of-david": $fa-var-star-of-david, + "person-military-rifle": $fa-var-person-military-rifle, + "cart-shopping": $fa-var-cart-shopping, + "shopping-cart": $fa-var-shopping-cart, + "vials": $fa-var-vials, + "plug-circle-plus": $fa-var-plug-circle-plus, + "place-of-worship": $fa-var-place-of-worship, + "grip-vertical": $fa-var-grip-vertical, + "arrow-turn-up": $fa-var-arrow-turn-up, + "level-up": $fa-var-level-up, + "u": $fa-var-u, + "square-root-variable": $fa-var-square-root-variable, + "square-root-alt": $fa-var-square-root-alt, + "clock": $fa-var-clock, + "clock-four": $fa-var-clock-four, + "backward-step": $fa-var-backward-step, + "step-backward": $fa-var-step-backward, + "pallet": $fa-var-pallet, + "faucet": $fa-var-faucet, + "baseball-bat-ball": $fa-var-baseball-bat-ball, + "s": $fa-var-s, + "timeline": $fa-var-timeline, + "keyboard": $fa-var-keyboard, + "caret-down": $fa-var-caret-down, + "house-chimney-medical": $fa-var-house-chimney-medical, + "clinic-medical": $fa-var-clinic-medical, + "temperature-three-quarters": $fa-var-temperature-three-quarters, + "temperature-3": $fa-var-temperature-3, + "thermometer-3": $fa-var-thermometer-3, + "thermometer-three-quarters": $fa-var-thermometer-three-quarters, + "mobile-screen": $fa-var-mobile-screen, + "mobile-android-alt": $fa-var-mobile-android-alt, + "plane-up": $fa-var-plane-up, + "piggy-bank": $fa-var-piggy-bank, + "battery-half": $fa-var-battery-half, + "battery-3": $fa-var-battery-3, + "mountain-city": $fa-var-mountain-city, + "coins": $fa-var-coins, + "khanda": $fa-var-khanda, + "sliders": $fa-var-sliders, + "sliders-h": $fa-var-sliders-h, + "folder-tree": $fa-var-folder-tree, + "network-wired": $fa-var-network-wired, + "map-pin": $fa-var-map-pin, + "hamsa": $fa-var-hamsa, + "cent-sign": $fa-var-cent-sign, + "flask": $fa-var-flask, + "person-pregnant": $fa-var-person-pregnant, + "wand-sparkles": $fa-var-wand-sparkles, + "ellipsis-vertical": $fa-var-ellipsis-vertical, + "ellipsis-v": $fa-var-ellipsis-v, + "ticket": $fa-var-ticket, + "power-off": $fa-var-power-off, + "right-long": $fa-var-right-long, + "long-arrow-alt-right": $fa-var-long-arrow-alt-right, + "flag-usa": $fa-var-flag-usa, + "laptop-file": $fa-var-laptop-file, + "tty": $fa-var-tty, + "teletype": $fa-var-teletype, + "diagram-next": $fa-var-diagram-next, + "person-rifle": $fa-var-person-rifle, + "house-medical-circle-exclamation": $fa-var-house-medical-circle-exclamation, + "closed-captioning": $fa-var-closed-captioning, + "person-hiking": $fa-var-person-hiking, + "hiking": $fa-var-hiking, + "venus-double": $fa-var-venus-double, + "images": $fa-var-images, + "calculator": $fa-var-calculator, + "people-pulling": $fa-var-people-pulling, + "n": $fa-var-n, + "cable-car": $fa-var-cable-car, + "tram": $fa-var-tram, + "cloud-rain": $fa-var-cloud-rain, + "building-circle-xmark": $fa-var-building-circle-xmark, + "ship": $fa-var-ship, + "arrows-down-to-line": $fa-var-arrows-down-to-line, + "download": $fa-var-download, + "face-grin": $fa-var-face-grin, + "grin": $fa-var-grin, + "delete-left": $fa-var-delete-left, + "backspace": $fa-var-backspace, + "eye-dropper": $fa-var-eye-dropper, + "eye-dropper-empty": $fa-var-eye-dropper-empty, + "eyedropper": $fa-var-eyedropper, + "file-circle-check": $fa-var-file-circle-check, + "forward": $fa-var-forward, + "mobile": $fa-var-mobile, + "mobile-android": $fa-var-mobile-android, + "mobile-phone": $fa-var-mobile-phone, + "face-meh": $fa-var-face-meh, + "meh": $fa-var-meh, + "align-center": $fa-var-align-center, + "book-skull": $fa-var-book-skull, + "book-dead": $fa-var-book-dead, + "id-card": $fa-var-id-card, + "drivers-license": $fa-var-drivers-license, + "outdent": $fa-var-outdent, + "dedent": $fa-var-dedent, + "heart-circle-exclamation": $fa-var-heart-circle-exclamation, + "house": $fa-var-house, + "home": $fa-var-home, + "home-alt": $fa-var-home-alt, + "home-lg-alt": $fa-var-home-lg-alt, + "calendar-week": $fa-var-calendar-week, + "laptop-medical": $fa-var-laptop-medical, + "b": $fa-var-b, + "file-medical": $fa-var-file-medical, + "dice-one": $fa-var-dice-one, + "kiwi-bird": $fa-var-kiwi-bird, + "arrow-right-arrow-left": $fa-var-arrow-right-arrow-left, + "exchange": $fa-var-exchange, + "rotate-right": $fa-var-rotate-right, + "redo-alt": $fa-var-redo-alt, + "rotate-forward": $fa-var-rotate-forward, + "utensils": $fa-var-utensils, + "cutlery": $fa-var-cutlery, + "arrow-up-wide-short": $fa-var-arrow-up-wide-short, + "sort-amount-up": $fa-var-sort-amount-up, + "mill-sign": $fa-var-mill-sign, + "bowl-rice": $fa-var-bowl-rice, + "skull": $fa-var-skull, + "tower-broadcast": $fa-var-tower-broadcast, + "broadcast-tower": $fa-var-broadcast-tower, + "truck-pickup": $fa-var-truck-pickup, + "up-long": $fa-var-up-long, + "long-arrow-alt-up": $fa-var-long-arrow-alt-up, + "stop": $fa-var-stop, + "code-merge": $fa-var-code-merge, + "upload": $fa-var-upload, + "hurricane": $fa-var-hurricane, + "mound": $fa-var-mound, + "toilet-portable": $fa-var-toilet-portable, + "compact-disc": $fa-var-compact-disc, + "file-arrow-down": $fa-var-file-arrow-down, + "file-download": $fa-var-file-download, + "caravan": $fa-var-caravan, + "shield-cat": $fa-var-shield-cat, + "bolt": $fa-var-bolt, + "zap": $fa-var-zap, + "glass-water": $fa-var-glass-water, + "oil-well": $fa-var-oil-well, + "vault": $fa-var-vault, + "mars": $fa-var-mars, + "toilet": $fa-var-toilet, + "plane-circle-xmark": $fa-var-plane-circle-xmark, + "yen-sign": $fa-var-yen-sign, + "cny": $fa-var-cny, + "jpy": $fa-var-jpy, + "rmb": $fa-var-rmb, + "yen": $fa-var-yen, + "ruble-sign": $fa-var-ruble-sign, + "rouble": $fa-var-rouble, + "rub": $fa-var-rub, + "ruble": $fa-var-ruble, + "sun": $fa-var-sun, + "guitar": $fa-var-guitar, + "face-laugh-wink": $fa-var-face-laugh-wink, + "laugh-wink": $fa-var-laugh-wink, + "horse-head": $fa-var-horse-head, + "bore-hole": $fa-var-bore-hole, + "industry": $fa-var-industry, + "circle-down": $fa-var-circle-down, + "arrow-alt-circle-down": $fa-var-arrow-alt-circle-down, + "arrows-turn-to-dots": $fa-var-arrows-turn-to-dots, + "florin-sign": $fa-var-florin-sign, + "arrow-down-short-wide": $fa-var-arrow-down-short-wide, + "sort-amount-desc": $fa-var-sort-amount-desc, + "sort-amount-down-alt": $fa-var-sort-amount-down-alt, + "less-than": $fa-var-less-than, + "angle-down": $fa-var-angle-down, + "car-tunnel": $fa-var-car-tunnel, + "head-side-cough": $fa-var-head-side-cough, + "grip-lines": $fa-var-grip-lines, + "thumbs-down": $fa-var-thumbs-down, + "user-lock": $fa-var-user-lock, + "arrow-right-long": $fa-var-arrow-right-long, + "long-arrow-right": $fa-var-long-arrow-right, + "anchor-circle-xmark": $fa-var-anchor-circle-xmark, + "ellipsis": $fa-var-ellipsis, + "ellipsis-h": $fa-var-ellipsis-h, + "chess-pawn": $fa-var-chess-pawn, + "kit-medical": $fa-var-kit-medical, + "first-aid": $fa-var-first-aid, + "person-through-window": $fa-var-person-through-window, + "toolbox": $fa-var-toolbox, + "hands-holding-circle": $fa-var-hands-holding-circle, + "bug": $fa-var-bug, + "credit-card": $fa-var-credit-card, + "credit-card-alt": $fa-var-credit-card-alt, + "car": $fa-var-car, + "automobile": $fa-var-automobile, + "hand-holding-hand": $fa-var-hand-holding-hand, + "book-open-reader": $fa-var-book-open-reader, + "book-reader": $fa-var-book-reader, + "mountain-sun": $fa-var-mountain-sun, + "arrows-left-right-to-line": $fa-var-arrows-left-right-to-line, + "dice-d20": $fa-var-dice-d20, + "truck-droplet": $fa-var-truck-droplet, + "file-circle-xmark": $fa-var-file-circle-xmark, + "temperature-arrow-up": $fa-var-temperature-arrow-up, + "temperature-up": $fa-var-temperature-up, + "medal": $fa-var-medal, + "bed": $fa-var-bed, + "square-h": $fa-var-square-h, + "h-square": $fa-var-h-square, + "podcast": $fa-var-podcast, + "temperature-full": $fa-var-temperature-full, + "temperature-4": $fa-var-temperature-4, + "thermometer-4": $fa-var-thermometer-4, + "thermometer-full": $fa-var-thermometer-full, + "bell": $fa-var-bell, + "superscript": $fa-var-superscript, + "plug-circle-xmark": $fa-var-plug-circle-xmark, + "star-of-life": $fa-var-star-of-life, + "phone-slash": $fa-var-phone-slash, + "paint-roller": $fa-var-paint-roller, + "handshake-angle": $fa-var-handshake-angle, + "hands-helping": $fa-var-hands-helping, + "location-dot": $fa-var-location-dot, + "map-marker-alt": $fa-var-map-marker-alt, + "file": $fa-var-file, + "greater-than": $fa-var-greater-than, + "person-swimming": $fa-var-person-swimming, + "swimmer": $fa-var-swimmer, + "arrow-down": $fa-var-arrow-down, + "droplet": $fa-var-droplet, + "tint": $fa-var-tint, + "eraser": $fa-var-eraser, + "earth-americas": $fa-var-earth-americas, + "earth": $fa-var-earth, + "earth-america": $fa-var-earth-america, + "globe-americas": $fa-var-globe-americas, + "person-burst": $fa-var-person-burst, + "dove": $fa-var-dove, + "battery-empty": $fa-var-battery-empty, + "battery-0": $fa-var-battery-0, + "socks": $fa-var-socks, + "inbox": $fa-var-inbox, + "section": $fa-var-section, + "gauge-high": $fa-var-gauge-high, + "tachometer-alt": $fa-var-tachometer-alt, + "tachometer-alt-fast": $fa-var-tachometer-alt-fast, + "envelope-open-text": $fa-var-envelope-open-text, + "hospital": $fa-var-hospital, + "hospital-alt": $fa-var-hospital-alt, + "hospital-wide": $fa-var-hospital-wide, + "wine-bottle": $fa-var-wine-bottle, + "chess-rook": $fa-var-chess-rook, + "bars-staggered": $fa-var-bars-staggered, + "reorder": $fa-var-reorder, + "stream": $fa-var-stream, + "dharmachakra": $fa-var-dharmachakra, + "hotdog": $fa-var-hotdog, + "person-walking-with-cane": $fa-var-person-walking-with-cane, + "blind": $fa-var-blind, + "drum": $fa-var-drum, + "ice-cream": $fa-var-ice-cream, + "heart-circle-bolt": $fa-var-heart-circle-bolt, + "fax": $fa-var-fax, + "paragraph": $fa-var-paragraph, + "check-to-slot": $fa-var-check-to-slot, + "vote-yea": $fa-var-vote-yea, + "star-half": $fa-var-star-half, + "boxes-stacked": $fa-var-boxes-stacked, + "boxes": $fa-var-boxes, + "boxes-alt": $fa-var-boxes-alt, + "link": $fa-var-link, + "chain": $fa-var-chain, + "ear-listen": $fa-var-ear-listen, + "assistive-listening-systems": $fa-var-assistive-listening-systems, + "tree-city": $fa-var-tree-city, + "play": $fa-var-play, + "font": $fa-var-font, + "rupiah-sign": $fa-var-rupiah-sign, + "magnifying-glass": $fa-var-magnifying-glass, + "search": $fa-var-search, + "table-tennis-paddle-ball": $fa-var-table-tennis-paddle-ball, + "ping-pong-paddle-ball": $fa-var-ping-pong-paddle-ball, + "table-tennis": $fa-var-table-tennis, + "person-dots-from-line": $fa-var-person-dots-from-line, + "diagnoses": $fa-var-diagnoses, + "trash-can-arrow-up": $fa-var-trash-can-arrow-up, + "trash-restore-alt": $fa-var-trash-restore-alt, + "naira-sign": $fa-var-naira-sign, + "cart-arrow-down": $fa-var-cart-arrow-down, + "walkie-talkie": $fa-var-walkie-talkie, + "file-pen": $fa-var-file-pen, + "file-edit": $fa-var-file-edit, + "receipt": $fa-var-receipt, + "square-pen": $fa-var-square-pen, + "pen-square": $fa-var-pen-square, + "pencil-square": $fa-var-pencil-square, + "suitcase-rolling": $fa-var-suitcase-rolling, + "person-circle-exclamation": $fa-var-person-circle-exclamation, + "chevron-down": $fa-var-chevron-down, + "battery-full": $fa-var-battery-full, + "battery": $fa-var-battery, + "battery-5": $fa-var-battery-5, + "skull-crossbones": $fa-var-skull-crossbones, + "code-compare": $fa-var-code-compare, + "list-ul": $fa-var-list-ul, + "list-dots": $fa-var-list-dots, + "school-lock": $fa-var-school-lock, + "tower-cell": $fa-var-tower-cell, + "down-long": $fa-var-down-long, + "long-arrow-alt-down": $fa-var-long-arrow-alt-down, + "ranking-star": $fa-var-ranking-star, + "chess-king": $fa-var-chess-king, + "person-harassing": $fa-var-person-harassing, + "brazilian-real-sign": $fa-var-brazilian-real-sign, + "landmark-dome": $fa-var-landmark-dome, + "landmark-alt": $fa-var-landmark-alt, + "arrow-up": $fa-var-arrow-up, + "tv": $fa-var-tv, + "television": $fa-var-television, + "tv-alt": $fa-var-tv-alt, + "shrimp": $fa-var-shrimp, + "list-check": $fa-var-list-check, + "tasks": $fa-var-tasks, + "jug-detergent": $fa-var-jug-detergent, + "circle-user": $fa-var-circle-user, + "user-circle": $fa-var-user-circle, + "user-shield": $fa-var-user-shield, + "wind": $fa-var-wind, + "car-burst": $fa-var-car-burst, + "car-crash": $fa-var-car-crash, + "y": $fa-var-y, + "person-snowboarding": $fa-var-person-snowboarding, + "snowboarding": $fa-var-snowboarding, + "truck-fast": $fa-var-truck-fast, + "shipping-fast": $fa-var-shipping-fast, + "fish": $fa-var-fish, + "user-graduate": $fa-var-user-graduate, + "circle-half-stroke": $fa-var-circle-half-stroke, + "adjust": $fa-var-adjust, + "clapperboard": $fa-var-clapperboard, + "circle-radiation": $fa-var-circle-radiation, + "radiation-alt": $fa-var-radiation-alt, + "baseball": $fa-var-baseball, + "baseball-ball": $fa-var-baseball-ball, + "jet-fighter-up": $fa-var-jet-fighter-up, + "diagram-project": $fa-var-diagram-project, + "project-diagram": $fa-var-project-diagram, + "copy": $fa-var-copy, + "volume-xmark": $fa-var-volume-xmark, + "volume-mute": $fa-var-volume-mute, + "volume-times": $fa-var-volume-times, + "hand-sparkles": $fa-var-hand-sparkles, + "grip": $fa-var-grip, + "grip-horizontal": $fa-var-grip-horizontal, + "share-from-square": $fa-var-share-from-square, + "share-square": $fa-var-share-square, + "child-combatant": $fa-var-child-combatant, + "child-rifle": $fa-var-child-rifle, + "gun": $fa-var-gun, + "square-phone": $fa-var-square-phone, + "phone-square": $fa-var-phone-square, + "plus": $fa-var-plus, + "add": $fa-var-add, + "expand": $fa-var-expand, + "computer": $fa-var-computer, + "xmark": $fa-var-xmark, + "close": $fa-var-close, + "multiply": $fa-var-multiply, + "remove": $fa-var-remove, + "times": $fa-var-times, + "arrows-up-down-left-right": $fa-var-arrows-up-down-left-right, + "arrows": $fa-var-arrows, + "chalkboard-user": $fa-var-chalkboard-user, + "chalkboard-teacher": $fa-var-chalkboard-teacher, + "peso-sign": $fa-var-peso-sign, + "building-shield": $fa-var-building-shield, + "baby": $fa-var-baby, + "users-line": $fa-var-users-line, + "quote-left": $fa-var-quote-left, + "quote-left-alt": $fa-var-quote-left-alt, + "tractor": $fa-var-tractor, + "trash-arrow-up": $fa-var-trash-arrow-up, + "trash-restore": $fa-var-trash-restore, + "arrow-down-up-lock": $fa-var-arrow-down-up-lock, + "lines-leaning": $fa-var-lines-leaning, + "ruler-combined": $fa-var-ruler-combined, + "copyright": $fa-var-copyright, + "equals": $fa-var-equals, + "blender": $fa-var-blender, + "teeth": $fa-var-teeth, + "shekel-sign": $fa-var-shekel-sign, + "ils": $fa-var-ils, + "shekel": $fa-var-shekel, + "sheqel": $fa-var-sheqel, + "sheqel-sign": $fa-var-sheqel-sign, + "map": $fa-var-map, + "rocket": $fa-var-rocket, + "photo-film": $fa-var-photo-film, + "photo-video": $fa-var-photo-video, + "folder-minus": $fa-var-folder-minus, + "store": $fa-var-store, + "arrow-trend-up": $fa-var-arrow-trend-up, + "plug-circle-minus": $fa-var-plug-circle-minus, + "sign-hanging": $fa-var-sign-hanging, + "sign": $fa-var-sign, + "bezier-curve": $fa-var-bezier-curve, + "bell-slash": $fa-var-bell-slash, + "tablet": $fa-var-tablet, + "tablet-android": $fa-var-tablet-android, + "school-flag": $fa-var-school-flag, + "fill": $fa-var-fill, + "angle-up": $fa-var-angle-up, + "drumstick-bite": $fa-var-drumstick-bite, + "holly-berry": $fa-var-holly-berry, + "chevron-left": $fa-var-chevron-left, + "bacteria": $fa-var-bacteria, + "hand-lizard": $fa-var-hand-lizard, + "notdef": $fa-var-notdef, + "disease": $fa-var-disease, + "briefcase-medical": $fa-var-briefcase-medical, + "genderless": $fa-var-genderless, + "chevron-right": $fa-var-chevron-right, + "retweet": $fa-var-retweet, + "car-rear": $fa-var-car-rear, + "car-alt": $fa-var-car-alt, + "pump-soap": $fa-var-pump-soap, + "video-slash": $fa-var-video-slash, + "battery-quarter": $fa-var-battery-quarter, + "battery-2": $fa-var-battery-2, + "radio": $fa-var-radio, + "baby-carriage": $fa-var-baby-carriage, + "carriage-baby": $fa-var-carriage-baby, + "traffic-light": $fa-var-traffic-light, + "thermometer": $fa-var-thermometer, + "vr-cardboard": $fa-var-vr-cardboard, + "hand-middle-finger": $fa-var-hand-middle-finger, + "percent": $fa-var-percent, + "percentage": $fa-var-percentage, + "truck-moving": $fa-var-truck-moving, + "glass-water-droplet": $fa-var-glass-water-droplet, + "display": $fa-var-display, + "face-smile": $fa-var-face-smile, + "smile": $fa-var-smile, + "thumbtack": $fa-var-thumbtack, + "thumb-tack": $fa-var-thumb-tack, + "trophy": $fa-var-trophy, + "person-praying": $fa-var-person-praying, + "pray": $fa-var-pray, + "hammer": $fa-var-hammer, + "hand-peace": $fa-var-hand-peace, + "rotate": $fa-var-rotate, + "sync-alt": $fa-var-sync-alt, + "spinner": $fa-var-spinner, + "robot": $fa-var-robot, + "peace": $fa-var-peace, + "gears": $fa-var-gears, + "cogs": $fa-var-cogs, + "warehouse": $fa-var-warehouse, + "arrow-up-right-dots": $fa-var-arrow-up-right-dots, + "splotch": $fa-var-splotch, + "face-grin-hearts": $fa-var-face-grin-hearts, + "grin-hearts": $fa-var-grin-hearts, + "dice-four": $fa-var-dice-four, + "sim-card": $fa-var-sim-card, + "transgender": $fa-var-transgender, + "transgender-alt": $fa-var-transgender-alt, + "mercury": $fa-var-mercury, + "arrow-turn-down": $fa-var-arrow-turn-down, + "level-down": $fa-var-level-down, + "person-falling-burst": $fa-var-person-falling-burst, + "award": $fa-var-award, + "ticket-simple": $fa-var-ticket-simple, + "ticket-alt": $fa-var-ticket-alt, + "building": $fa-var-building, + "angles-left": $fa-var-angles-left, + "angle-double-left": $fa-var-angle-double-left, + "qrcode": $fa-var-qrcode, + "clock-rotate-left": $fa-var-clock-rotate-left, + "history": $fa-var-history, + "face-grin-beam-sweat": $fa-var-face-grin-beam-sweat, + "grin-beam-sweat": $fa-var-grin-beam-sweat, + "file-export": $fa-var-file-export, + "arrow-right-from-file": $fa-var-arrow-right-from-file, + "shield": $fa-var-shield, + "shield-blank": $fa-var-shield-blank, + "arrow-up-short-wide": $fa-var-arrow-up-short-wide, + "sort-amount-up-alt": $fa-var-sort-amount-up-alt, + "house-medical": $fa-var-house-medical, + "golf-ball-tee": $fa-var-golf-ball-tee, + "golf-ball": $fa-var-golf-ball, + "circle-chevron-left": $fa-var-circle-chevron-left, + "chevron-circle-left": $fa-var-chevron-circle-left, + "house-chimney-window": $fa-var-house-chimney-window, + "pen-nib": $fa-var-pen-nib, + "tent-arrow-turn-left": $fa-var-tent-arrow-turn-left, + "tents": $fa-var-tents, + "wand-magic": $fa-var-wand-magic, + "magic": $fa-var-magic, + "dog": $fa-var-dog, + "carrot": $fa-var-carrot, + "moon": $fa-var-moon, + "wine-glass-empty": $fa-var-wine-glass-empty, + "wine-glass-alt": $fa-var-wine-glass-alt, + "cheese": $fa-var-cheese, + "yin-yang": $fa-var-yin-yang, + "music": $fa-var-music, + "code-commit": $fa-var-code-commit, + "temperature-low": $fa-var-temperature-low, + "person-biking": $fa-var-person-biking, + "biking": $fa-var-biking, + "broom": $fa-var-broom, + "shield-heart": $fa-var-shield-heart, + "gopuram": $fa-var-gopuram, + "earth-oceania": $fa-var-earth-oceania, + "globe-oceania": $fa-var-globe-oceania, + "square-xmark": $fa-var-square-xmark, + "times-square": $fa-var-times-square, + "xmark-square": $fa-var-xmark-square, + "hashtag": $fa-var-hashtag, + "up-right-and-down-left-from-center": $fa-var-up-right-and-down-left-from-center, + "expand-alt": $fa-var-expand-alt, + "oil-can": $fa-var-oil-can, + "t": $fa-var-t, + "hippo": $fa-var-hippo, + "chart-column": $fa-var-chart-column, + "infinity": $fa-var-infinity, + "vial-circle-check": $fa-var-vial-circle-check, + "person-arrow-down-to-line": $fa-var-person-arrow-down-to-line, + "voicemail": $fa-var-voicemail, + "fan": $fa-var-fan, + "person-walking-luggage": $fa-var-person-walking-luggage, + "up-down": $fa-var-up-down, + "arrows-alt-v": $fa-var-arrows-alt-v, + "cloud-moon-rain": $fa-var-cloud-moon-rain, + "calendar": $fa-var-calendar, + "trailer": $fa-var-trailer, + "bahai": $fa-var-bahai, + "haykal": $fa-var-haykal, + "sd-card": $fa-var-sd-card, + "dragon": $fa-var-dragon, + "shoe-prints": $fa-var-shoe-prints, + "circle-plus": $fa-var-circle-plus, + "plus-circle": $fa-var-plus-circle, + "face-grin-tongue-wink": $fa-var-face-grin-tongue-wink, + "grin-tongue-wink": $fa-var-grin-tongue-wink, + "hand-holding": $fa-var-hand-holding, + "plug-circle-exclamation": $fa-var-plug-circle-exclamation, + "link-slash": $fa-var-link-slash, + "chain-broken": $fa-var-chain-broken, + "chain-slash": $fa-var-chain-slash, + "unlink": $fa-var-unlink, + "clone": $fa-var-clone, + "person-walking-arrow-loop-left": $fa-var-person-walking-arrow-loop-left, + "arrow-up-z-a": $fa-var-arrow-up-z-a, + "sort-alpha-up-alt": $fa-var-sort-alpha-up-alt, + "fire-flame-curved": $fa-var-fire-flame-curved, + "fire-alt": $fa-var-fire-alt, + "tornado": $fa-var-tornado, + "file-circle-plus": $fa-var-file-circle-plus, + "book-quran": $fa-var-book-quran, + "quran": $fa-var-quran, + "anchor": $fa-var-anchor, + "border-all": $fa-var-border-all, + "face-angry": $fa-var-face-angry, + "angry": $fa-var-angry, + "cookie-bite": $fa-var-cookie-bite, + "arrow-trend-down": $fa-var-arrow-trend-down, + "rss": $fa-var-rss, + "feed": $fa-var-feed, + "draw-polygon": $fa-var-draw-polygon, + "scale-balanced": $fa-var-scale-balanced, + "balance-scale": $fa-var-balance-scale, + "gauge-simple-high": $fa-var-gauge-simple-high, + "tachometer": $fa-var-tachometer, + "tachometer-fast": $fa-var-tachometer-fast, + "shower": $fa-var-shower, + "desktop": $fa-var-desktop, + "desktop-alt": $fa-var-desktop-alt, + "m": $fa-var-m, + "table-list": $fa-var-table-list, + "th-list": $fa-var-th-list, + "comment-sms": $fa-var-comment-sms, + "sms": $fa-var-sms, + "book": $fa-var-book, + "user-plus": $fa-var-user-plus, + "check": $fa-var-check, + "battery-three-quarters": $fa-var-battery-three-quarters, + "battery-4": $fa-var-battery-4, + "house-circle-check": $fa-var-house-circle-check, + "angle-left": $fa-var-angle-left, + "diagram-successor": $fa-var-diagram-successor, + "truck-arrow-right": $fa-var-truck-arrow-right, + "arrows-split-up-and-left": $fa-var-arrows-split-up-and-left, + "hand-fist": $fa-var-hand-fist, + "fist-raised": $fa-var-fist-raised, + "cloud-moon": $fa-var-cloud-moon, + "briefcase": $fa-var-briefcase, + "person-falling": $fa-var-person-falling, + "image-portrait": $fa-var-image-portrait, + "portrait": $fa-var-portrait, + "user-tag": $fa-var-user-tag, + "rug": $fa-var-rug, + "earth-europe": $fa-var-earth-europe, + "globe-europe": $fa-var-globe-europe, + "cart-flatbed-suitcase": $fa-var-cart-flatbed-suitcase, + "luggage-cart": $fa-var-luggage-cart, + "rectangle-xmark": $fa-var-rectangle-xmark, + "rectangle-times": $fa-var-rectangle-times, + "times-rectangle": $fa-var-times-rectangle, + "window-close": $fa-var-window-close, + "baht-sign": $fa-var-baht-sign, + "book-open": $fa-var-book-open, + "book-journal-whills": $fa-var-book-journal-whills, + "journal-whills": $fa-var-journal-whills, + "handcuffs": $fa-var-handcuffs, + "triangle-exclamation": $fa-var-triangle-exclamation, + "exclamation-triangle": $fa-var-exclamation-triangle, + "warning": $fa-var-warning, + "database": $fa-var-database, + "share": $fa-var-share, + "arrow-turn-right": $fa-var-arrow-turn-right, + "mail-forward": $fa-var-mail-forward, + "bottle-droplet": $fa-var-bottle-droplet, + "mask-face": $fa-var-mask-face, + "hill-rockslide": $fa-var-hill-rockslide, + "right-left": $fa-var-right-left, + "exchange-alt": $fa-var-exchange-alt, + "paper-plane": $fa-var-paper-plane, + "road-circle-exclamation": $fa-var-road-circle-exclamation, + "dungeon": $fa-var-dungeon, + "align-right": $fa-var-align-right, + "money-bill-1-wave": $fa-var-money-bill-1-wave, + "money-bill-wave-alt": $fa-var-money-bill-wave-alt, + "life-ring": $fa-var-life-ring, + "hands": $fa-var-hands, + "sign-language": $fa-var-sign-language, + "signing": $fa-var-signing, + "calendar-day": $fa-var-calendar-day, + "water-ladder": $fa-var-water-ladder, + "ladder-water": $fa-var-ladder-water, + "swimming-pool": $fa-var-swimming-pool, + "arrows-up-down": $fa-var-arrows-up-down, + "arrows-v": $fa-var-arrows-v, + "face-grimace": $fa-var-face-grimace, + "grimace": $fa-var-grimace, + "wheelchair-move": $fa-var-wheelchair-move, + "wheelchair-alt": $fa-var-wheelchair-alt, + "turn-down": $fa-var-turn-down, + "level-down-alt": $fa-var-level-down-alt, + "person-walking-arrow-right": $fa-var-person-walking-arrow-right, + "square-envelope": $fa-var-square-envelope, + "envelope-square": $fa-var-envelope-square, + "dice": $fa-var-dice, + "bowling-ball": $fa-var-bowling-ball, + "brain": $fa-var-brain, + "bandage": $fa-var-bandage, + "band-aid": $fa-var-band-aid, + "calendar-minus": $fa-var-calendar-minus, + "circle-xmark": $fa-var-circle-xmark, + "times-circle": $fa-var-times-circle, + "xmark-circle": $fa-var-xmark-circle, + "gifts": $fa-var-gifts, + "hotel": $fa-var-hotel, + "earth-asia": $fa-var-earth-asia, + "globe-asia": $fa-var-globe-asia, + "id-card-clip": $fa-var-id-card-clip, + "id-card-alt": $fa-var-id-card-alt, + "magnifying-glass-plus": $fa-var-magnifying-glass-plus, + "search-plus": $fa-var-search-plus, + "thumbs-up": $fa-var-thumbs-up, + "user-clock": $fa-var-user-clock, + "hand-dots": $fa-var-hand-dots, + "allergies": $fa-var-allergies, + "file-invoice": $fa-var-file-invoice, + "window-minimize": $fa-var-window-minimize, + "mug-saucer": $fa-var-mug-saucer, + "coffee": $fa-var-coffee, + "brush": $fa-var-brush, + "mask": $fa-var-mask, + "magnifying-glass-minus": $fa-var-magnifying-glass-minus, + "search-minus": $fa-var-search-minus, + "ruler-vertical": $fa-var-ruler-vertical, + "user-large": $fa-var-user-large, + "user-alt": $fa-var-user-alt, + "train-tram": $fa-var-train-tram, + "user-nurse": $fa-var-user-nurse, + "syringe": $fa-var-syringe, + "cloud-sun": $fa-var-cloud-sun, + "stopwatch-20": $fa-var-stopwatch-20, + "square-full": $fa-var-square-full, + "magnet": $fa-var-magnet, + "jar": $fa-var-jar, + "note-sticky": $fa-var-note-sticky, + "sticky-note": $fa-var-sticky-note, + "bug-slash": $fa-var-bug-slash, + "arrow-up-from-water-pump": $fa-var-arrow-up-from-water-pump, + "bone": $fa-var-bone, + "user-injured": $fa-var-user-injured, + "face-sad-tear": $fa-var-face-sad-tear, + "sad-tear": $fa-var-sad-tear, + "plane": $fa-var-plane, + "tent-arrows-down": $fa-var-tent-arrows-down, + "exclamation": $fa-var-exclamation, + "arrows-spin": $fa-var-arrows-spin, + "print": $fa-var-print, + "turkish-lira-sign": $fa-var-turkish-lira-sign, + "try": $fa-var-try, + "turkish-lira": $fa-var-turkish-lira, + "dollar-sign": $fa-var-dollar-sign, + "dollar": $fa-var-dollar, + "usd": $fa-var-usd, + "x": $fa-var-x, + "magnifying-glass-dollar": $fa-var-magnifying-glass-dollar, + "search-dollar": $fa-var-search-dollar, + "users-gear": $fa-var-users-gear, + "users-cog": $fa-var-users-cog, + "person-military-pointing": $fa-var-person-military-pointing, + "building-columns": $fa-var-building-columns, + "bank": $fa-var-bank, + "institution": $fa-var-institution, + "museum": $fa-var-museum, + "university": $fa-var-university, + "umbrella": $fa-var-umbrella, + "trowel": $fa-var-trowel, + "d": $fa-var-d, + "stapler": $fa-var-stapler, + "masks-theater": $fa-var-masks-theater, + "theater-masks": $fa-var-theater-masks, + "kip-sign": $fa-var-kip-sign, + "hand-point-left": $fa-var-hand-point-left, + "handshake-simple": $fa-var-handshake-simple, + "handshake-alt": $fa-var-handshake-alt, + "jet-fighter": $fa-var-jet-fighter, + "fighter-jet": $fa-var-fighter-jet, + "square-share-nodes": $fa-var-square-share-nodes, + "share-alt-square": $fa-var-share-alt-square, + "barcode": $fa-var-barcode, + "plus-minus": $fa-var-plus-minus, + "video": $fa-var-video, + "video-camera": $fa-var-video-camera, + "graduation-cap": $fa-var-graduation-cap, + "mortar-board": $fa-var-mortar-board, + "hand-holding-medical": $fa-var-hand-holding-medical, + "person-circle-check": $fa-var-person-circle-check, + "turn-up": $fa-var-turn-up, + "level-up-alt": $fa-var-level-up-alt, +); + +$fa-brand-icons: ( + "monero": $fa-var-monero, + "hooli": $fa-var-hooli, + "yelp": $fa-var-yelp, + "cc-visa": $fa-var-cc-visa, + "lastfm": $fa-var-lastfm, + "shopware": $fa-var-shopware, + "creative-commons-nc": $fa-var-creative-commons-nc, + "aws": $fa-var-aws, + "redhat": $fa-var-redhat, + "yoast": $fa-var-yoast, + "cloudflare": $fa-var-cloudflare, + "ups": $fa-var-ups, + "wpexplorer": $fa-var-wpexplorer, + "dyalog": $fa-var-dyalog, + "bity": $fa-var-bity, + "stackpath": $fa-var-stackpath, + "buysellads": $fa-var-buysellads, + "first-order": $fa-var-first-order, + "modx": $fa-var-modx, + "guilded": $fa-var-guilded, + "vnv": $fa-var-vnv, + "square-js": $fa-var-square-js, + "js-square": $fa-var-js-square, + "microsoft": $fa-var-microsoft, + "qq": $fa-var-qq, + "orcid": $fa-var-orcid, + "java": $fa-var-java, + "invision": $fa-var-invision, + "creative-commons-pd-alt": $fa-var-creative-commons-pd-alt, + "centercode": $fa-var-centercode, + "glide-g": $fa-var-glide-g, + "drupal": $fa-var-drupal, + "hire-a-helper": $fa-var-hire-a-helper, + "creative-commons-by": $fa-var-creative-commons-by, + "unity": $fa-var-unity, + "whmcs": $fa-var-whmcs, + "rocketchat": $fa-var-rocketchat, + "vk": $fa-var-vk, + "untappd": $fa-var-untappd, + "mailchimp": $fa-var-mailchimp, + "css3-alt": $fa-var-css3-alt, + "square-reddit": $fa-var-square-reddit, + "reddit-square": $fa-var-reddit-square, + "vimeo-v": $fa-var-vimeo-v, + "contao": $fa-var-contao, + "square-font-awesome": $fa-var-square-font-awesome, + "deskpro": $fa-var-deskpro, + "sistrix": $fa-var-sistrix, + "square-instagram": $fa-var-square-instagram, + "instagram-square": $fa-var-instagram-square, + "battle-net": $fa-var-battle-net, + "the-red-yeti": $fa-var-the-red-yeti, + "square-hacker-news": $fa-var-square-hacker-news, + "hacker-news-square": $fa-var-hacker-news-square, + "edge": $fa-var-edge, + "napster": $fa-var-napster, + "square-snapchat": $fa-var-square-snapchat, + "snapchat-square": $fa-var-snapchat-square, + "google-plus-g": $fa-var-google-plus-g, + "artstation": $fa-var-artstation, + "markdown": $fa-var-markdown, + "sourcetree": $fa-var-sourcetree, + "google-plus": $fa-var-google-plus, + "diaspora": $fa-var-diaspora, + "foursquare": $fa-var-foursquare, + "stack-overflow": $fa-var-stack-overflow, + "github-alt": $fa-var-github-alt, + "phoenix-squadron": $fa-var-phoenix-squadron, + "pagelines": $fa-var-pagelines, + "algolia": $fa-var-algolia, + "red-river": $fa-var-red-river, + "creative-commons-sa": $fa-var-creative-commons-sa, + "safari": $fa-var-safari, + "google": $fa-var-google, + "square-font-awesome-stroke": $fa-var-square-font-awesome-stroke, + "font-awesome-alt": $fa-var-font-awesome-alt, + "atlassian": $fa-var-atlassian, + "linkedin-in": $fa-var-linkedin-in, + "digital-ocean": $fa-var-digital-ocean, + "nimblr": $fa-var-nimblr, + "chromecast": $fa-var-chromecast, + "evernote": $fa-var-evernote, + "hacker-news": $fa-var-hacker-news, + "creative-commons-sampling": $fa-var-creative-commons-sampling, + "adversal": $fa-var-adversal, + "creative-commons": $fa-var-creative-commons, + "watchman-monitoring": $fa-var-watchman-monitoring, + "fonticons": $fa-var-fonticons, + "weixin": $fa-var-weixin, + "shirtsinbulk": $fa-var-shirtsinbulk, + "codepen": $fa-var-codepen, + "git-alt": $fa-var-git-alt, + "lyft": $fa-var-lyft, + "rev": $fa-var-rev, + "windows": $fa-var-windows, + "wizards-of-the-coast": $fa-var-wizards-of-the-coast, + "square-viadeo": $fa-var-square-viadeo, + "viadeo-square": $fa-var-viadeo-square, + "meetup": $fa-var-meetup, + "centos": $fa-var-centos, + "adn": $fa-var-adn, + "cloudsmith": $fa-var-cloudsmith, + "pied-piper-alt": $fa-var-pied-piper-alt, + "square-dribbble": $fa-var-square-dribbble, + "dribbble-square": $fa-var-dribbble-square, + "codiepie": $fa-var-codiepie, + "node": $fa-var-node, + "mix": $fa-var-mix, + "steam": $fa-var-steam, + "cc-apple-pay": $fa-var-cc-apple-pay, + "scribd": $fa-var-scribd, + "openid": $fa-var-openid, + "instalod": $fa-var-instalod, + "expeditedssl": $fa-var-expeditedssl, + "sellcast": $fa-var-sellcast, + "square-twitter": $fa-var-square-twitter, + "twitter-square": $fa-var-twitter-square, + "r-project": $fa-var-r-project, + "delicious": $fa-var-delicious, + "freebsd": $fa-var-freebsd, + "vuejs": $fa-var-vuejs, + "accusoft": $fa-var-accusoft, + "ioxhost": $fa-var-ioxhost, + "fonticons-fi": $fa-var-fonticons-fi, + "app-store": $fa-var-app-store, + "cc-mastercard": $fa-var-cc-mastercard, + "itunes-note": $fa-var-itunes-note, + "golang": $fa-var-golang, + "kickstarter": $fa-var-kickstarter, + "grav": $fa-var-grav, + "weibo": $fa-var-weibo, + "uncharted": $fa-var-uncharted, + "firstdraft": $fa-var-firstdraft, + "square-youtube": $fa-var-square-youtube, + "youtube-square": $fa-var-youtube-square, + "wikipedia-w": $fa-var-wikipedia-w, + "wpressr": $fa-var-wpressr, + "rendact": $fa-var-rendact, + "angellist": $fa-var-angellist, + "galactic-republic": $fa-var-galactic-republic, + "nfc-directional": $fa-var-nfc-directional, + "skype": $fa-var-skype, + "joget": $fa-var-joget, + "fedora": $fa-var-fedora, + "stripe-s": $fa-var-stripe-s, + "meta": $fa-var-meta, + "laravel": $fa-var-laravel, + "hotjar": $fa-var-hotjar, + "bluetooth-b": $fa-var-bluetooth-b, + "sticker-mule": $fa-var-sticker-mule, + "creative-commons-zero": $fa-var-creative-commons-zero, + "hips": $fa-var-hips, + "behance": $fa-var-behance, + "reddit": $fa-var-reddit, + "discord": $fa-var-discord, + "chrome": $fa-var-chrome, + "app-store-ios": $fa-var-app-store-ios, + "cc-discover": $fa-var-cc-discover, + "wpbeginner": $fa-var-wpbeginner, + "confluence": $fa-var-confluence, + "mdb": $fa-var-mdb, + "dochub": $fa-var-dochub, + "accessible-icon": $fa-var-accessible-icon, + "ebay": $fa-var-ebay, + "amazon": $fa-var-amazon, + "unsplash": $fa-var-unsplash, + "yarn": $fa-var-yarn, + "square-steam": $fa-var-square-steam, + "steam-square": $fa-var-steam-square, + "500px": $fa-var-500px, + "square-vimeo": $fa-var-square-vimeo, + "vimeo-square": $fa-var-vimeo-square, + "asymmetrik": $fa-var-asymmetrik, + "font-awesome": $fa-var-font-awesome, + "font-awesome-flag": $fa-var-font-awesome-flag, + "font-awesome-logo-full": $fa-var-font-awesome-logo-full, + "gratipay": $fa-var-gratipay, + "apple": $fa-var-apple, + "hive": $fa-var-hive, + "gitkraken": $fa-var-gitkraken, + "keybase": $fa-var-keybase, + "apple-pay": $fa-var-apple-pay, + "padlet": $fa-var-padlet, + "amazon-pay": $fa-var-amazon-pay, + "square-github": $fa-var-square-github, + "github-square": $fa-var-github-square, + "stumbleupon": $fa-var-stumbleupon, + "fedex": $fa-var-fedex, + "phoenix-framework": $fa-var-phoenix-framework, + "shopify": $fa-var-shopify, + "neos": $fa-var-neos, + "hackerrank": $fa-var-hackerrank, + "researchgate": $fa-var-researchgate, + "swift": $fa-var-swift, + "angular": $fa-var-angular, + "speakap": $fa-var-speakap, + "angrycreative": $fa-var-angrycreative, + "y-combinator": $fa-var-y-combinator, + "empire": $fa-var-empire, + "envira": $fa-var-envira, + "square-gitlab": $fa-var-square-gitlab, + "gitlab-square": $fa-var-gitlab-square, + "studiovinari": $fa-var-studiovinari, + "pied-piper": $fa-var-pied-piper, + "wordpress": $fa-var-wordpress, + "product-hunt": $fa-var-product-hunt, + "firefox": $fa-var-firefox, + "linode": $fa-var-linode, + "goodreads": $fa-var-goodreads, + "square-odnoklassniki": $fa-var-square-odnoklassniki, + "odnoklassniki-square": $fa-var-odnoklassniki-square, + "jsfiddle": $fa-var-jsfiddle, + "sith": $fa-var-sith, + "themeisle": $fa-var-themeisle, + "page4": $fa-var-page4, + "hashnode": $fa-var-hashnode, + "react": $fa-var-react, + "cc-paypal": $fa-var-cc-paypal, + "squarespace": $fa-var-squarespace, + "cc-stripe": $fa-var-cc-stripe, + "creative-commons-share": $fa-var-creative-commons-share, + "bitcoin": $fa-var-bitcoin, + "keycdn": $fa-var-keycdn, + "opera": $fa-var-opera, + "itch-io": $fa-var-itch-io, + "umbraco": $fa-var-umbraco, + "galactic-senate": $fa-var-galactic-senate, + "ubuntu": $fa-var-ubuntu, + "draft2digital": $fa-var-draft2digital, + "stripe": $fa-var-stripe, + "houzz": $fa-var-houzz, + "gg": $fa-var-gg, + "dhl": $fa-var-dhl, + "square-pinterest": $fa-var-square-pinterest, + "pinterest-square": $fa-var-pinterest-square, + "xing": $fa-var-xing, + "blackberry": $fa-var-blackberry, + "creative-commons-pd": $fa-var-creative-commons-pd, + "playstation": $fa-var-playstation, + "quinscape": $fa-var-quinscape, + "less": $fa-var-less, + "blogger-b": $fa-var-blogger-b, + "opencart": $fa-var-opencart, + "vine": $fa-var-vine, + "paypal": $fa-var-paypal, + "gitlab": $fa-var-gitlab, + "typo3": $fa-var-typo3, + "reddit-alien": $fa-var-reddit-alien, + "yahoo": $fa-var-yahoo, + "dailymotion": $fa-var-dailymotion, + "affiliatetheme": $fa-var-affiliatetheme, + "pied-piper-pp": $fa-var-pied-piper-pp, + "bootstrap": $fa-var-bootstrap, + "odnoklassniki": $fa-var-odnoklassniki, + "nfc-symbol": $fa-var-nfc-symbol, + "ethereum": $fa-var-ethereum, + "speaker-deck": $fa-var-speaker-deck, + "creative-commons-nc-eu": $fa-var-creative-commons-nc-eu, + "patreon": $fa-var-patreon, + "avianex": $fa-var-avianex, + "ello": $fa-var-ello, + "gofore": $fa-var-gofore, + "bimobject": $fa-var-bimobject, + "facebook-f": $fa-var-facebook-f, + "square-google-plus": $fa-var-square-google-plus, + "google-plus-square": $fa-var-google-plus-square, + "mandalorian": $fa-var-mandalorian, + "first-order-alt": $fa-var-first-order-alt, + "osi": $fa-var-osi, + "google-wallet": $fa-var-google-wallet, + "d-and-d-beyond": $fa-var-d-and-d-beyond, + "periscope": $fa-var-periscope, + "fulcrum": $fa-var-fulcrum, + "cloudscale": $fa-var-cloudscale, + "forumbee": $fa-var-forumbee, + "mizuni": $fa-var-mizuni, + "schlix": $fa-var-schlix, + "square-xing": $fa-var-square-xing, + "xing-square": $fa-var-xing-square, + "bandcamp": $fa-var-bandcamp, + "wpforms": $fa-var-wpforms, + "cloudversify": $fa-var-cloudversify, + "usps": $fa-var-usps, + "megaport": $fa-var-megaport, + "magento": $fa-var-magento, + "spotify": $fa-var-spotify, + "optin-monster": $fa-var-optin-monster, + "fly": $fa-var-fly, + "aviato": $fa-var-aviato, + "itunes": $fa-var-itunes, + "cuttlefish": $fa-var-cuttlefish, + "blogger": $fa-var-blogger, + "flickr": $fa-var-flickr, + "viber": $fa-var-viber, + "soundcloud": $fa-var-soundcloud, + "digg": $fa-var-digg, + "tencent-weibo": $fa-var-tencent-weibo, + "symfony": $fa-var-symfony, + "maxcdn": $fa-var-maxcdn, + "etsy": $fa-var-etsy, + "facebook-messenger": $fa-var-facebook-messenger, + "audible": $fa-var-audible, + "think-peaks": $fa-var-think-peaks, + "bilibili": $fa-var-bilibili, + "erlang": $fa-var-erlang, + "cotton-bureau": $fa-var-cotton-bureau, + "dashcube": $fa-var-dashcube, + "42-group": $fa-var-42-group, + "innosoft": $fa-var-innosoft, + "stack-exchange": $fa-var-stack-exchange, + "elementor": $fa-var-elementor, + "square-pied-piper": $fa-var-square-pied-piper, + "pied-piper-square": $fa-var-pied-piper-square, + "creative-commons-nd": $fa-var-creative-commons-nd, + "palfed": $fa-var-palfed, + "superpowers": $fa-var-superpowers, + "resolving": $fa-var-resolving, + "xbox": $fa-var-xbox, + "searchengin": $fa-var-searchengin, + "tiktok": $fa-var-tiktok, + "square-facebook": $fa-var-square-facebook, + "facebook-square": $fa-var-facebook-square, + "renren": $fa-var-renren, + "linux": $fa-var-linux, + "glide": $fa-var-glide, + "linkedin": $fa-var-linkedin, + "hubspot": $fa-var-hubspot, + "deploydog": $fa-var-deploydog, + "twitch": $fa-var-twitch, + "ravelry": $fa-var-ravelry, + "mixer": $fa-var-mixer, + "square-lastfm": $fa-var-square-lastfm, + "lastfm-square": $fa-var-lastfm-square, + "vimeo": $fa-var-vimeo, + "mendeley": $fa-var-mendeley, + "uniregistry": $fa-var-uniregistry, + "figma": $fa-var-figma, + "creative-commons-remix": $fa-var-creative-commons-remix, + "cc-amazon-pay": $fa-var-cc-amazon-pay, + "dropbox": $fa-var-dropbox, + "instagram": $fa-var-instagram, + "cmplid": $fa-var-cmplid, + "facebook": $fa-var-facebook, + "gripfire": $fa-var-gripfire, + "jedi-order": $fa-var-jedi-order, + "uikit": $fa-var-uikit, + "fort-awesome-alt": $fa-var-fort-awesome-alt, + "phabricator": $fa-var-phabricator, + "ussunnah": $fa-var-ussunnah, + "earlybirds": $fa-var-earlybirds, + "trade-federation": $fa-var-trade-federation, + "autoprefixer": $fa-var-autoprefixer, + "whatsapp": $fa-var-whatsapp, + "slideshare": $fa-var-slideshare, + "google-play": $fa-var-google-play, + "viadeo": $fa-var-viadeo, + "line": $fa-var-line, + "google-drive": $fa-var-google-drive, + "servicestack": $fa-var-servicestack, + "simplybuilt": $fa-var-simplybuilt, + "bitbucket": $fa-var-bitbucket, + "imdb": $fa-var-imdb, + "deezer": $fa-var-deezer, + "raspberry-pi": $fa-var-raspberry-pi, + "jira": $fa-var-jira, + "docker": $fa-var-docker, + "screenpal": $fa-var-screenpal, + "bluetooth": $fa-var-bluetooth, + "gitter": $fa-var-gitter, + "d-and-d": $fa-var-d-and-d, + "microblog": $fa-var-microblog, + "cc-diners-club": $fa-var-cc-diners-club, + "gg-circle": $fa-var-gg-circle, + "pied-piper-hat": $fa-var-pied-piper-hat, + "kickstarter-k": $fa-var-kickstarter-k, + "yandex": $fa-var-yandex, + "readme": $fa-var-readme, + "html5": $fa-var-html5, + "sellsy": $fa-var-sellsy, + "sass": $fa-var-sass, + "wirsindhandwerk": $fa-var-wirsindhandwerk, + "wsh": $fa-var-wsh, + "buromobelexperte": $fa-var-buromobelexperte, + "salesforce": $fa-var-salesforce, + "octopus-deploy": $fa-var-octopus-deploy, + "medapps": $fa-var-medapps, + "ns8": $fa-var-ns8, + "pinterest-p": $fa-var-pinterest-p, + "apper": $fa-var-apper, + "fort-awesome": $fa-var-fort-awesome, + "waze": $fa-var-waze, + "cc-jcb": $fa-var-cc-jcb, + "snapchat": $fa-var-snapchat, + "snapchat-ghost": $fa-var-snapchat-ghost, + "fantasy-flight-games": $fa-var-fantasy-flight-games, + "rust": $fa-var-rust, + "wix": $fa-var-wix, + "square-behance": $fa-var-square-behance, + "behance-square": $fa-var-behance-square, + "supple": $fa-var-supple, + "rebel": $fa-var-rebel, + "css3": $fa-var-css3, + "staylinked": $fa-var-staylinked, + "kaggle": $fa-var-kaggle, + "space-awesome": $fa-var-space-awesome, + "deviantart": $fa-var-deviantart, + "cpanel": $fa-var-cpanel, + "goodreads-g": $fa-var-goodreads-g, + "square-git": $fa-var-square-git, + "git-square": $fa-var-git-square, + "square-tumblr": $fa-var-square-tumblr, + "tumblr-square": $fa-var-tumblr-square, + "trello": $fa-var-trello, + "creative-commons-nc-jp": $fa-var-creative-commons-nc-jp, + "get-pocket": $fa-var-get-pocket, + "perbyte": $fa-var-perbyte, + "grunt": $fa-var-grunt, + "weebly": $fa-var-weebly, + "connectdevelop": $fa-var-connectdevelop, + "leanpub": $fa-var-leanpub, + "black-tie": $fa-var-black-tie, + "themeco": $fa-var-themeco, + "python": $fa-var-python, + "android": $fa-var-android, + "bots": $fa-var-bots, + "free-code-camp": $fa-var-free-code-camp, + "hornbill": $fa-var-hornbill, + "js": $fa-var-js, + "ideal": $fa-var-ideal, + "git": $fa-var-git, + "dev": $fa-var-dev, + "sketch": $fa-var-sketch, + "yandex-international": $fa-var-yandex-international, + "cc-amex": $fa-var-cc-amex, + "uber": $fa-var-uber, + "github": $fa-var-github, + "php": $fa-var-php, + "alipay": $fa-var-alipay, + "youtube": $fa-var-youtube, + "skyatlas": $fa-var-skyatlas, + "firefox-browser": $fa-var-firefox-browser, + "replyd": $fa-var-replyd, + "suse": $fa-var-suse, + "jenkins": $fa-var-jenkins, + "twitter": $fa-var-twitter, + "rockrms": $fa-var-rockrms, + "pinterest": $fa-var-pinterest, + "buffer": $fa-var-buffer, + "npm": $fa-var-npm, + "yammer": $fa-var-yammer, + "btc": $fa-var-btc, + "dribbble": $fa-var-dribbble, + "stumbleupon-circle": $fa-var-stumbleupon-circle, + "internet-explorer": $fa-var-internet-explorer, + "stubber": $fa-var-stubber, + "telegram": $fa-var-telegram, + "telegram-plane": $fa-var-telegram-plane, + "old-republic": $fa-var-old-republic, + "odysee": $fa-var-odysee, + "square-whatsapp": $fa-var-square-whatsapp, + "whatsapp-square": $fa-var-whatsapp-square, + "node-js": $fa-var-node-js, + "edge-legacy": $fa-var-edge-legacy, + "slack": $fa-var-slack, + "slack-hash": $fa-var-slack-hash, + "medrt": $fa-var-medrt, + "usb": $fa-var-usb, + "tumblr": $fa-var-tumblr, + "vaadin": $fa-var-vaadin, + "quora": $fa-var-quora, + "reacteurope": $fa-var-reacteurope, + "medium": $fa-var-medium, + "medium-m": $fa-var-medium-m, + "amilia": $fa-var-amilia, + "mixcloud": $fa-var-mixcloud, + "flipboard": $fa-var-flipboard, + "viacoin": $fa-var-viacoin, + "critical-role": $fa-var-critical-role, + "sitrox": $fa-var-sitrox, + "discourse": $fa-var-discourse, + "joomla": $fa-var-joomla, + "mastodon": $fa-var-mastodon, + "airbnb": $fa-var-airbnb, + "wolf-pack-battalion": $fa-var-wolf-pack-battalion, + "buy-n-large": $fa-var-buy-n-large, + "gulp": $fa-var-gulp, + "creative-commons-sampling-plus": $fa-var-creative-commons-sampling-plus, + "strava": $fa-var-strava, + "ember": $fa-var-ember, + "canadian-maple-leaf": $fa-var-canadian-maple-leaf, + "teamspeak": $fa-var-teamspeak, + "pushed": $fa-var-pushed, + "wordpress-simple": $fa-var-wordpress-simple, + "nutritionix": $fa-var-nutritionix, + "wodu": $fa-var-wodu, + "google-pay": $fa-var-google-pay, + "intercom": $fa-var-intercom, + "zhihu": $fa-var-zhihu, + "korvue": $fa-var-korvue, + "pix": $fa-var-pix, + "steam-symbol": $fa-var-steam-symbol, +); diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/brands.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/brands.scss new file mode 100644 index 0000000..fcea462 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/brands.scss @@ -0,0 +1,30 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +@import 'functions'; +@import 'variables'; + +:root, :host { + --#{$fa-css-prefix}-style-family-brands: 'Font Awesome 6 Brands'; + --#{$fa-css-prefix}-font-brands: normal 400 1em/1 'Font Awesome 6 Brands'; +} + +@font-face { + font-family: 'Font Awesome 6 Brands'; + font-style: normal; + font-weight: 400; + font-display: $fa-font-display; + src: url('#{$fa-font-path}/fa-brands-400.woff2') format('woff2'), + url('#{$fa-font-path}/fa-brands-400.ttf') format('truetype'); +} + +.fab, +.#{$fa-css-prefix}-brands { + font-weight: 400; +} + +@each $name, $icon in $fa-brand-icons { + .#{$fa-css-prefix}-#{$name}:before { content: unquote("\"#{ $icon }\""); } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/fontawesome.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/fontawesome.scss new file mode 100644 index 0000000..ab4f133 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/fontawesome.scss @@ -0,0 +1,21 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +// Font Awesome core compile (Web Fonts-based) +// ------------------------- + +@import 'functions'; +@import 'variables'; +@import 'mixins'; +@import 'core'; +@import 'sizing'; +@import 'fixed-width'; +@import 'list'; +@import 'bordered-pulled'; +@import 'animated'; +@import 'rotated-flipped'; +@import 'stacked'; +@import 'icons'; +@import 'screen-reader'; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/regular.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/regular.scss new file mode 100644 index 0000000..40d1753 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/regular.scss @@ -0,0 +1,26 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +@import 'functions'; +@import 'variables'; + +:root, :host { + --#{$fa-css-prefix}-style-family-classic: '#{ $fa-style-family }'; + --#{$fa-css-prefix}-font-regular: normal 400 1em/1 '#{ $fa-style-family }'; +} + +@font-face { + font-family: 'Font Awesome 6 Free'; + font-style: normal; + font-weight: 400; + font-display: $fa-font-display; + src: url('#{$fa-font-path}/fa-regular-400.woff2') format('woff2'), + url('#{$fa-font-path}/fa-regular-400.ttf') format('truetype'); +} + +.far, +.#{$fa-css-prefix}-regular { + font-weight: 400; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/solid.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/solid.scss new file mode 100644 index 0000000..3dd635f --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/solid.scss @@ -0,0 +1,26 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +@import 'functions'; +@import 'variables'; + +:root, :host { + --#{$fa-css-prefix}-style-family-classic: '#{ $fa-style-family }'; + --#{$fa-css-prefix}-font-solid: normal 900 1em/1 '#{ $fa-style-family }'; +} + +@font-face { + font-family: 'Font Awesome 6 Free'; + font-style: normal; + font-weight: 900; + font-display: $fa-font-display; + src: url('#{$fa-font-path}/fa-solid-900.woff2') format('woff2'), + url('#{$fa-font-path}/fa-solid-900.ttf') format('truetype'); +} + +.fas, +.#{$fa-css-prefix}-solid { + font-weight: 900; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/v4-shims.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/v4-shims.scss new file mode 100644 index 0000000..7893e7c --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/scss/vendors/fontawesome-free/v4-shims.scss @@ -0,0 +1,11 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +// V4 shims compile (Web Fonts-based) +// ------------------------- + +@import 'functions'; +@import 'variables'; +@import 'shims'; diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/styles.scss b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/styles.scss new file mode 100644 index 0000000..b625805 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/src/styles.scss @@ -0,0 +1,14 @@ +@import 'scss/base/typography'; + +@import 'scss/vendors/fontawesome-free/fontawesome.scss'; +@import 'scss/vendors/fontawesome-free/brands.scss'; +@import 'scss/vendors/fontawesome-free/regular.scss'; +@import 'scss/vendors/fontawesome-free/solid.scss'; + +@import 'scss/vendors/material_io'; + +@import 'scss/shame'; + + +html, body { height: 100%; } +body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/test_and_report.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/test_and_report.bash new file mode 100644 index 0000000..5ba4c0d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/test_and_report.bash @@ -0,0 +1,3 @@ +#!/bin/bash + +ng test --no-watch --code-coverage --browsers Firefox diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/tsconfig.app.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/tsconfig.app.json new file mode 100644 index 0000000..7dc7284 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/tsconfig.app.json @@ -0,0 +1,18 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [ + "node" + ] + }, + "files": [ + "src/main.ts", + "src/main.server.ts", + "server.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/tsconfig.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/tsconfig.json new file mode 100644 index 0000000..678336b --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/tsconfig.json @@ -0,0 +1,32 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "sourceMap": true, + "declaration": false, + "experimentalDecorators": true, + "moduleResolution": "node", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "useDefineForClassFields": false, + "lib": [ + "ES2022", + "dom" + ] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/tsconfig.spec.json b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/tsconfig.spec.json new file mode 100644 index 0000000..be7e9da --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_client_write_and_read/write-and-read/write-and-read-frontend/tsconfig.spec.json @@ -0,0 +1,14 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/.gitignore b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/.gitignore new file mode 100644 index 0000000..839bdac --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/.gitignore @@ -0,0 +1,7 @@ +.env + +**/ca.key +**/ca.crt +**/ca.srl + +nginx/cert \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/ca-certificates/type b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/ca-certificates/type new file mode 100644 index 0000000..54619ed --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/ca-certificates/type @@ -0,0 +1 @@ +ca-certificates \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/docker-compose.yaml b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/docker-compose.yaml new file mode 100644 index 0000000..abe6e17 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/docker-compose.yaml @@ -0,0 +1,109 @@ +version: "2.4" + +services: + + ory-hydra-oauth2-example-resource-server-backend: + image: chistousov/ory-hydra-oauth2-example-resource-server-backend:1.0.0 + container_name: ory-hydra-oauth2-example-resource-server-backend + environment: + - LANG=en_US.UTF-8 + - LANGUAGE=en_US:en + + - BPL_JAVA_NMT_LEVEL=detail + + # ca add + - SERVICE_BINDING_ROOT=/bindings + + # health check + - THC_PATH=/actuator/health + - THC_PORT=8080 + + - JAVA_TOOL_OPTIONS= + -Dspring.profiles.active=prod + + -Dspring.security.oauth2.resourceserver.opaquetoken.client-id=${HYDRA_INTROSPECT_USER} + -Dspring.security.oauth2.resourceserver.opaquetoken.client-secret=${HYDRA_INTROSPECT_PASSWORD} + -Dspring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://${DNS_AUTHORIZATION_SERVER}/admin/oauth2/introspect + + user: "1005" + mem_limit: 1G + logging: + options: + max-size: "100m" + max-file: "1" + volumes: + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + - type: bind + source: ca-certificates + target: /bindings/ca-certificates + read_only: true + healthcheck: + test: "/cnb/process/health-check" + interval: 20s + timeout: 5s + retries: 5 + start_period: 40s + networks: + - app-network + extra_hosts: + - "${DNS_AUTHORIZATION_SERVER}:${IP_AUTHORIZATION_SERVER}" + + + ory-hydra-oauth2-example-resource-server-gateway-nginx: + image: nginx:1.25.3-bookworm + container_name: ory-hydra-oauth2-example-resource-server-gateway-nginx + healthcheck: + test: '[ -e /var/run/nginx.pid ] || exit 1' + interval: 20s + timeout: 5s + retries: 5 + start_period: 40s + ports: + - 443:443 + volumes: + - type: bind + source: nginx/nginx.conf + target: /etc/nginx/nginx.conf + read_only: true + + - type: bind + source: nginx/confs + target: /etc/nginx/conf.d + read_only: true + + - type: bind + source: nginx/cert + target: /cert + read_only: true + + - type: bind + source: /etc/timezone + target: /etc/timezone + read_only: true + + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + + restart: unless-stopped + depends_on: + ory-hydra-oauth2-example-resource-server-backend: + condition: service_healthy + logging: + options: + max-size: "100m" + max-file: "1" + mem_limit: 1G + networks: + - app-network + +networks: + app-network: diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/nginx/confs/default.conf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/nginx/confs/default.conf new file mode 100644 index 0000000..86124f6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/nginx/confs/default.conf @@ -0,0 +1,11 @@ +server { + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/nginx/confs/resource-server.conf.templ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/nginx/confs/resource-server.conf.templ new file mode 100644 index 0000000..f79dafb --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/nginx/confs/resource-server.conf.templ @@ -0,0 +1,36 @@ +server { + listen 443 ssl; + http2 on; + server_name ${DNS_RESOURCE_SERVER}; + + ssl_certificate /cert/${DNS_RESOURCE_SERVER}/${DNS_RESOURCE_SERVER}.crt; + ssl_certificate_key /cert/${DNS_RESOURCE_SERVER}/${DNS_RESOURCE_SERVER}.key; + + add_header Strict-Transport-Security "max-age=31536000; preload" always; + add_header X-Content-Type-Options "nosniff"; + add_header X-Frame-Options "DENY"; + add_header X-XSS-Protection "1; mode=block"; + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + # Docker DNS + resolver 127.0.0.11; + + location /api/ { + set $ory_hydra_oauth2_example_resource_server_backend http://ory-hydra-oauth2-example-resource-server-backend:8080; + rewrite /api/(.*) /$1 break; + proxy_pass $ory_hydra_oauth2_example_resource_server_backend; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto-Version $http2; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Prefix /api; + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/nginx/nginx.conf b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/nginx/nginx.conf new file mode 100644 index 0000000..2238388 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/nginx/nginx.conf @@ -0,0 +1,37 @@ + +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + variables_hash_bucket_size 512; + + proxy_buffer_size 128k; + proxy_buffers 4 256k; + proxy_busy_buffers_size 256k; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/.editorconfig b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/.editorconfig new file mode 100644 index 0000000..59d9a3a --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/.gitignore b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/build.gradle b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/build.gradle new file mode 100644 index 0000000..2234db6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/build.gradle @@ -0,0 +1,85 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '2.7.13' + id 'io.spring.dependency-management' version '1.0.15.RELEASE' + id 'jacoco' +} + +ext { + + httpproxy = "$System.env.HTTP_PROXY" ?: "" + httpsproxy = "$System.env.HTTPS_PROXY" ?: "" + noproxy = "$System.env.NO_PROXY" ?: "" + + repoImage = "$System.env.REPO_IMAGE" ?: "" + projectName = "$System.env.PROJECT_NAME" ?: "" + versionProj = "$System.env.VERSION" ?: "" + +} + +group = 'com.github.chistousov' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '11' + withSourcesJar() +} + +compileJava.options.encoding = "UTF-8" +compileTestJava.options.encoding = "UTF-8" + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +jacocoTestReport { + dependsOn test + reports { + xml.required = true + } +} + + +repositories { + mavenCentral() +} + +bootBuildImage { + imageName = "${repoImage}/${projectName}:${versionProj}" + environment = [ + // "HTTP_PROXY" : httpproxy.toString(), + // "HTTPS_PROXY" : httpsproxy.toString(), + // "NO_PROXY" : noproxy.toString(), + // add health check for docker + "BP_HEALTH_CHECKER_ENABLED": "true" + ] + buildpacks = ["urn:cnb:builder:paketo-buildpacks/java", "gcr.io/paketo-buildpacks/health-checker:latest"] +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-webflux' + + implementation 'org.springdoc:springdoc-openapi-webflux-ui:1.7.0' + implementation 'org.springdoc:springdoc-openapi-security:1.7.0' + + compileOnly 'org.projectlombok:lombok' + + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' + annotationProcessor 'org.projectlombok:lombok' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'io.projectreactor:reactor-test' + testImplementation 'org.springframework.security:spring-security-test' + +} + + +tasks.named('test') { + useJUnitPlatform() + finalizedBy jacocoTestReport +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/build_image.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/build_image.bash new file mode 100644 index 0000000..f524cd4 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/build_image.bash @@ -0,0 +1,20 @@ +#!/bin/bash + +# export HTTP_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +# export HTTPS_PROXY="http://proxyuser:proxypass@192.168.20.4:8822/" +# export NO_PROXY="localhost,127.0.0.1" + +export REPO_IMAGE="chistousov" +export PROJECT_NAME="ory-hydra-oauth2-example-resource-server-backend" +export VERSION="1.0.0" + +docker pull paketobuildpacks/builder-jammy-full:0.3.316 + +./gradlew clean test +./gradlew bootBuildImage --builder=paketobuildpacks/builder-jammy-full:0.3.316 + +# publish in docker hub +# docker login +# docker push $REPO_IMAGE/$PROJECT_NAME:$VERSION +# docker logout +# docker rmi $REPO_IMAGE/$PROJECT_NAME:$VERSION diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/gradle/wrapper/gradle-wrapper.jar b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..c1962a7 Binary files /dev/null and b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/gradle/wrapper/gradle-wrapper.jar differ diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/gradle/wrapper/gradle-wrapper.properties b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..37aef8d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/gradlew b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/gradlew new file mode 100644 index 0000000..aeb74cb --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/gradlew @@ -0,0 +1,245 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/gradlew.bat b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/lombok.config b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/lombok.config new file mode 100644 index 0000000..8f7e8aa --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/lombok.config @@ -0,0 +1 @@ +lombok.addLombokGeneratedAnnotation = true \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/settings.gradle b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/settings.gradle new file mode 100644 index 0000000..501d5a7 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'resource-server' diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/ResourceServerApplication.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/ResourceServerApplication.java new file mode 100644 index 0000000..6a7b088 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/ResourceServerApplication.java @@ -0,0 +1,16 @@ +package com.github.chistousov.resource_server; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.github.chistousov.resource_server.jacoco_ignore.ExcludeFromJacocoGeneratedReport; + +@SpringBootApplication +@ExcludeFromJacocoGeneratedReport +public class ResourceServerApplication { + + public static void main(String[] args) { + SpringApplication.run(ResourceServerApplication.class, args); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/SpringSecurityConfiguration.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/SpringSecurityConfiguration.java new file mode 100644 index 0000000..44c27d3 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/SpringSecurityConfiguration.java @@ -0,0 +1,42 @@ +package com.github.chistousov.resource_server; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.web.server.SecurityWebFilterChain; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.security.SecurityScheme; + +@OpenAPIDefinition(info = @Info(description = "Example resource server"), security = { + @SecurityRequirement(name = "Introspect OAuth 2.0") +}) +@SecurityScheme(name = "Introspect OAuth 2.0", scheme = "bearer", type = SecuritySchemeType.HTTP, bearerFormat = "opaque token", in = SecuritySchemeIn.HEADER) +@Configuration +@EnableWebFluxSecurity +public class SpringSecurityConfiguration { + + @Bean + public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { + http + .authorizeExchange( + ex -> ex + .pathMatchers("/actuator/health", "/v3/api-docs/**", "/webjars/swagger-ui/**") + .permitAll() + .pathMatchers("/statistics") + .hasAuthority("SCOPE_read") + .pathMatchers("/calculate") + .hasAuthority("SCOPE_write")) + .csrf(csrf -> csrf.disable()) + .oauth2ResourceServer() + .opaqueToken(); + + return http.build(); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/controllers/CalculateController.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/controllers/CalculateController.java new file mode 100644 index 0000000..7fa079d --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/controllers/CalculateController.java @@ -0,0 +1,29 @@ +package com.github.chistousov.resource_server.controllers; + +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.github.chistousov.resource_server.services.PointService; + +import io.swagger.v3.oas.annotations.Operation; +import lombok.extern.slf4j.Slf4j; + +@RestController +@RequestMapping("/calculate") +@Slf4j +public class CalculateController { + + private PointService pointService; + + public CalculateController(PointService pointService) { + this.pointService = pointService; + } + + @Operation(summary = "Some calculate") + @PutMapping + public void calculate() { + this.pointService.calculateNewPoint(); + log.info("calculate new point"); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/controllers/GlobalExceptionHandler.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/controllers/GlobalExceptionHandler.java new file mode 100644 index 0000000..ce1b964 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/controllers/GlobalExceptionHandler.java @@ -0,0 +1,75 @@ +package com.github.chistousov.resource_server.controllers; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.bind.support.WebExchangeBindException; + +import com.github.chistousov.resource_server.jacoco_ignore.ExcludeFromJacocoGeneratedReport; + +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@RestControllerAdvice +@Slf4j +@ExcludeFromJacocoGeneratedReport +public class GlobalExceptionHandler { + + private static final String TEXT_PLAIN_CHARSET_UTF_8 = "text/plain;charset=utf-8"; + private static final String CONTENT_TYPE = "Content-Type"; + + // invalid model handler + @ExceptionHandler(WebExchangeBindException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity> handleException(WebExchangeBindException e) { + var errors = e.getBindingResult() + .getAllErrors() + .stream() + .map(DefaultMessageSourceResolvable::getDefaultMessage) + .collect(Collectors.toList()); + return ResponseEntity.badRequest().body(errors); + + } + + @ExceptionHandler(IllegalArgumentException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Mono illegalArgumentPerfom(ServerHttpRequest req, ServerHttpResponse response, + IllegalArgumentException ex) { + + response.getHeaders().set(CONTENT_TYPE, TEXT_PLAIN_CHARSET_UTF_8); + response.setStatusCode(HttpStatus.BAD_REQUEST); + + byte[] bodyResponse = "Request received with incorrect data ".getBytes(StandardCharsets.UTF_8); + DataBuffer buffer = response.bufferFactory().wrap(bodyResponse); + + return response.writeWith(Flux.just(buffer)) + .doOnEach(el -> log.error(req.getPath().toString() + " ", ex)); + + } + + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public Mono commonHandler(ServerHttpRequest req, ServerHttpResponse response, Exception ex) { + + response.getHeaders().set(CONTENT_TYPE, TEXT_PLAIN_CHARSET_UTF_8); + response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + + byte[] bodyResponse = "Error in server ".getBytes(StandardCharsets.UTF_8); + DataBuffer buffer = response.bufferFactory().wrap(bodyResponse); + + return response.writeWith(Flux.just(buffer)) + .doOnEach(el -> log.error(req.getPath().toString() + " ", ex)); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/controllers/StatisticsController.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/controllers/StatisticsController.java new file mode 100644 index 0000000..f958bbe --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/controllers/StatisticsController.java @@ -0,0 +1,34 @@ +package com.github.chistousov.resource_server.controllers; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.github.chistousov.resource_server.models.Point; +import com.github.chistousov.resource_server.services.PointService; + +import io.swagger.v3.oas.annotations.Operation; +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Flux; + +@Controller +@RequestMapping("/statistics") +@Slf4j +public class StatisticsController { + + private PointService pointService; + + public StatisticsController(PointService pointService) { + this.pointService = pointService; + } + + @Operation(summary = "Get statistics") + @GetMapping + @ResponseBody + public Flux getStatistics() { + return this.pointService + .getPoints() + .doOnComplete(() -> log.info("Response: get points ")); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/jacoco_ignore/ExcludeFromJacocoGeneratedReport.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/jacoco_ignore/ExcludeFromJacocoGeneratedReport.java new file mode 100644 index 0000000..d312bf2 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/jacoco_ignore/ExcludeFromJacocoGeneratedReport.java @@ -0,0 +1,21 @@ +package com.github.chistousov.resource_server.jacoco_ignore; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marking with this annotation, we ignore code coverage by JaCoCo tests for a + * class or method. + * Помечая данной аннотацией, мы игнорируем для класса или метода покрытие кода + * тестами JaCoCo + */ +@Documented +@Retention(RUNTIME) +@Target({ TYPE, METHOD }) +public @interface ExcludeFromJacocoGeneratedReport { +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/models/Point.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/models/Point.java new file mode 100644 index 0000000..b06cf88 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/models/Point.java @@ -0,0 +1,19 @@ +package com.github.chistousov.resource_server.models; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Schema(description = "Graph point") +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +@EqualsAndHashCode +public class Point { + private double x; + private double y; +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/services/PointService.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/services/PointService.java new file mode 100644 index 0000000..ad1bfbc --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/java/com/github/chistousov/resource_server/services/PointService.java @@ -0,0 +1,47 @@ +package com.github.chistousov.resource_server.services; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import org.springframework.stereotype.Service; + +import com.github.chistousov.resource_server.jacoco_ignore.ExcludeFromJacocoGeneratedReport; +import com.github.chistousov.resource_server.models.Point; + +import reactor.core.publisher.Flux; + +@Service +@ExcludeFromJacocoGeneratedReport +public class PointService { + + private List points; + + private double nextX; + + private Random random = new Random(); + + public PointService() { + + this.points = new ArrayList<>(); + this.points.add(new Point(1, 2.3)); + this.points.add(new Point(2, 1.7)); + this.points.add(new Point(3, 5.4)); + this.points.add(new Point(4, 6.8)); + this.points.add(new Point(5, 4.2)); + this.points.add(new Point(6, 3.6)); + + this.nextX = 7 + (2 * random.nextDouble()); + + } + + public Flux getPoints() { + return Flux.fromIterable(points); + } + + public void calculateNewPoint() { + points.add(new Point(nextX, 20 * random.nextDouble())); + nextX += 2 * random.nextDouble(); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/resources/application-prod.yml b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/resources/application-prod.yml new file mode 100644 index 0000000..c56eee6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/resources/application-prod.yml @@ -0,0 +1,3 @@ +logging: + level: + root: info \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/resources/application.yml b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/resources/application.yml new file mode 100644 index 0000000..2b45adf --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/resources/application.yml @@ -0,0 +1,22 @@ +application: + title: Resource Server OAuth 2.0 Example Ory Hydra + +spring: + output: + ansi: + enabled: always + +logging: + level: + root: debug + +server: + forward-headers-strategy: framework + error: + include-stacktrace: never + +springdoc: + swagger-ui: + display-operation-id: true + csrf: + enabled: true \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/resources/banner.txt b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/resources/banner.txt new file mode 100644 index 0000000..a4e1230 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/main/resources/banner.txt @@ -0,0 +1,7 @@ +${AnsiColor.BRIGHT_CYAN} +,---.| o | +| |---..,---.|--- ,---.. .,---.,---.. , +| | ||`---.| | || |`---.| | \ / +`---'` '``---'`---'`---'`---'`---'`---' `' +${AnsiBackground.WHITE}${AnsiColor.BRIGHT_BLACK}${application.title} +Powered by Spring Boot ${spring-boot.version}${AnsiColor.DEFAULT}${AnsiBackground.DEFAULT} \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/test/java/com/github/chistousov/resource_server/controllers/CalculateControllerTest.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/test/java/com/github/chistousov/resource_server/controllers/CalculateControllerTest.java new file mode 100644 index 0000000..979a471 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/test/java/com/github/chistousov/resource_server/controllers/CalculateControllerTest.java @@ -0,0 +1,63 @@ +package com.github.chistousov.resource_server.controllers; + +import static org.mockito.BDDMockito.then; + +import java.io.UnsupportedEncodingException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.web.reactive.server.WebTestClient; + +import com.github.chistousov.resource_server.SpringSecurityConfiguration; +import com.github.chistousov.resource_server.services.PointService; + +@WebFluxTest +@Import(SpringSecurityConfiguration.class) +public class CalculateControllerTest { + + @DynamicPropertySource + public static void settings(DynamicPropertyRegistry registry) + throws UnsupportedEncodingException { + + registry.add("spring.security.oauth2.resourceserver.opaquetoken.client-id", () -> "1"); + registry.add("spring.security.oauth2.resourceserver.opaquetoken.client-secret", () -> "1"); + registry.add("spring.security.oauth2.resourceserver.opaquetoken.introspection-uri", () -> "1"); + + } + + @Autowired + private WebTestClient thisServerWebTestClient; + + @MockBean + private PointService pointService; + + @Test + @DisplayName("calculate") + void testPostCalculate() { + // given (instead of when) + + // when + + thisServerWebTestClient + .mutateWith(SecurityMockServerConfigurers.mockOpaqueToken() + .authorities(new SimpleGrantedAuthority("SCOPE_write"))) + .put() + .uri("/calculate") + .exchange() + .expectStatus() + .isOk(); + + // then (instead of verify + + then(pointService) + .should() + .calculateNewPoint(); + } +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/test/java/com/github/chistousov/resource_server/controllers/StatisticsControllerTest.java b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/test/java/com/github/chistousov/resource_server/controllers/StatisticsControllerTest.java new file mode 100644 index 0000000..eab2874 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/src/test/java/com/github/chistousov/resource_server/controllers/StatisticsControllerTest.java @@ -0,0 +1,85 @@ +package com.github.chistousov.resource_server.controllers; + +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; + +import java.io.UnsupportedEncodingException; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.web.reactive.server.WebTestClient; + +import com.github.chistousov.resource_server.SpringSecurityConfiguration; +import com.github.chistousov.resource_server.models.Point; +import com.github.chistousov.resource_server.services.PointService; + +import reactor.core.publisher.Flux; + +@WebFluxTest +@Import(SpringSecurityConfiguration.class) +public class StatisticsControllerTest { + + @DynamicPropertySource + public static void settings(DynamicPropertyRegistry registry) + throws UnsupportedEncodingException { + + registry.add("spring.security.oauth2.resourceserver.opaquetoken.client-id", () -> "1"); + registry.add("spring.security.oauth2.resourceserver.opaquetoken.client-secret", () -> "1"); + registry.add("spring.security.oauth2.resourceserver.opaquetoken.introspection-uri", () -> "1"); + + } + + @Autowired + private WebTestClient thisServerWebTestClient; + + @MockBean + private PointService pointService; + + @Test + @DisplayName("get statistics") + void testGetStatistics() { + + // given (instead of when) + + final List points = List.of( + new Point(1, 2.3), + new Point(2, 1.7), + new Point(3, 5.4), + new Point(4, 6.8), + new Point(5, 4.2), + new Point(6, 3.6)); + + final Flux pointsFlux = Flux.fromIterable(points); + + given(pointService.getPoints()).willReturn(pointsFlux); + + // when + + thisServerWebTestClient + .mutateWith(SecurityMockServerConfigurers.mockOpaqueToken() + .authorities(new SimpleGrantedAuthority("SCOPE_read"))) + .get() + .uri("/statistics") + .exchange() + .expectStatus() + .isOk() + .expectBodyList(Point.class) + .isEqualTo(points); + + // then (instead of verify + + then(pointService) + .should() + .getPoints(); + } + +} diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/test_and_report.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/test_and_report.bash new file mode 100644 index 0000000..ef6d5fe --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/ory_hydra_oauth2_example_resource_server/resource-server/test_and_report.bash @@ -0,0 +1,3 @@ +#!/bin/bash + +./gradlew test jacocoTestReport diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/start.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/start.bash new file mode 100644 index 0000000..a2c5c24 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/start.bash @@ -0,0 +1,330 @@ +#!/bin/bash + +generate_CA() { + + if [ ! -f $1.crt ]; + then + # generating a key for a CA + # генерация ключа для CA + openssl genrsa -out $1.key 4096 + + # generating a certificate for a CA + # формирование сертификата для CA + openssl req -x509 -new -nodes -sha512 -days 999999 \ + -subj "/C=RU/ST=Stavropol region/L=Stavropol/O=Some ORG/OU=Some dep/CN=some cn gateway nginx for $1" \ + -key $1.key \ + -out $1.crt + else + echo "CA gateway nginx for $1 is exists" + fi +} + + +generate_cert(){ + +if [ ! -f $1/nginx/cert/${2}/${2}.crt ]; then + + mkdir -p $1/nginx/cert/${2} + + # private key + # закрытый ключ + openssl genrsa -out $1/nginx/cert/${2}/${2}.key 4096 + + # Request for Certification (CSR) + # запрос на сертификацию (CSR) + openssl req -sha512 -new \ + -subj "/C=RU/ST=Stavropol region/L=Stavropol/O=Some ORG/OU=Some dep/CN=${2}" \ + -key $1/nginx/cert/${2}/${2}.key \ + -out ${2}.csr + + + # v3 extension for the certificate + # расширение v3 для сертификата + cat >v3-${2}.ext <<-EOF +authorityKeyIdentifier=keyid,issuer +basicConstraints=CA:FALSE +keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment +extendedKeyUsage = serverAuth +subjectAltName = @alt_names +[alt_names] +DNS.1=${2} +EOF + # certificate generation + # генерация сертификата + openssl x509 -req -sha512 -days 999999 \ + -extfile v3-${2}.ext \ + -CA $3.crt -CAkey $3.key -CAcreateserial \ + -in ${2}.csr \ + -out $1/nginx/cert/${2}/${2}.crt + + rm -rf ${2}.csr + rm v3-${2}.ext + + else + echo "$1/nginx/cert/${2}/${2}.crt is exists" + fi + +} + +source .env + +echo "------------------ AUTHORIZATION SERVER ------------------" + +CA_AUTH_SERVER_NAME=ca_authorization_server +AUTH_DIR=ory_hydra_oauth2_example_authorization_server +REMOTE_AUTH_DIR=authorization_server + +echo "------------------ generate .env (docker compose) for authorization server ------------------" + +HYDRA_DEPENDS_ON_MIGRATE="service_completed_successfully" + +echo HYDRA_DEPENDS_ON_MIGRATE=${HYDRA_DEPENDS_ON_MIGRATE} >>.env_auth_server +echo USER_DATA_POSTGRESQL_PASSWORD=${USER_DATA_POSTGRESQL_PASSWORD} >>.env_auth_server +echo HYDRA_POSTGRESQL_PASSWORD=${HYDRA_POSTGRESQL_PASSWORD} >>.env_auth_server +echo HYDRA_SECRETS_COOKIE=${HYDRA_SECRETS_COOKIE} >>.env_auth_server +echo HYDRA_SECRETS_SYSTEM=${HYDRA_SECRETS_SYSTEM} >>.env_auth_server + +echo "------------------------------------------------------" + +echo "------------------ generate login and password introspect for authorization server ------------------" +htpasswd -bcB -C 10 htpasswd_introspect ${HYDRA_INTROSPECT_USER} ${HYDRA_INTROSPECT_PASSWORD} +mv htpasswd_introspect ${AUTH_DIR}/nginx/confs/htpasswd_introspect +echo "------------------------------------------------------" + +echo "------------------ generate CA gateway nginx for authorization server ------------------" +generate_CA "${CA_AUTH_SERVER_NAME}" +echo "------------------------------------------------------" + +echo "------------------ generate certs gateway nginx for authorization server ------------------" +generate_cert "${AUTH_DIR}" "${DNS_AUTHORIZATION_SERVER}" "${CA_AUTH_SERVER_NAME}" +echo "------------------------------------------------------" + +echo "------------------ create folder for authorization server------------------" +ssh ${USER_AUTHORIZATION_SERVER}@${IP_AUTHORIZATION_SERVER} "mkdir ${REMOTE_AUTH_DIR}" +echo "------------------------------------------------------" + +echo "------------------ remote copy .env for authorization server------------------" +scp .env_auth_server ${USER_AUTHORIZATION_SERVER}@${IP_AUTHORIZATION_SERVER}:${REMOTE_AUTH_DIR}/.env +rm .env_auth_server +echo "------------------------------------------------------" + +echo "------------------ remote copy hydra for authorization server------------------" +scp -r ${AUTH_DIR}/hydra ${USER_AUTHORIZATION_SERVER}@${IP_AUTHORIZATION_SERVER}:${REMOTE_AUTH_DIR}/hydra +echo "------------------------------------------------------" + +echo "------------------ remote copy nginx for authorization server------------------" +DNS_AUTHORIZATION_SERVER=${DNS_AUTHORIZATION_SERVER} envsubst '$DNS_AUTHORIZATION_SERVER' < ${AUTH_DIR}/nginx/confs/authorization-server.conf.templ > ${AUTH_DIR}/nginx/confs/authorization-server.conf +scp -r ${AUTH_DIR}/nginx ${USER_AUTHORIZATION_SERVER}@${IP_AUTHORIZATION_SERVER}:${REMOTE_AUTH_DIR}/nginx +rm ${AUTH_DIR}/nginx/confs/authorization-server.conf +rm ${AUTH_DIR}/nginx/confs/htpasswd_introspect +echo "------------------------------------------------------" + +echo "------------------ remote copy user_data for authorization server------------------" +scp -r ${AUTH_DIR}/user_data ${USER_AUTHORIZATION_SERVER}@${IP_AUTHORIZATION_SERVER}:${REMOTE_AUTH_DIR}/user_data +echo "------------------------------------------------------" + +echo "------------------ remote copy docker-compose.yaml for authorization server------------------" +scp ${AUTH_DIR}/docker-compose.yaml ${USER_AUTHORIZATION_SERVER}@${IP_AUTHORIZATION_SERVER}:${REMOTE_AUTH_DIR}/docker-compose.yaml +scp ${AUTH_DIR}/docker-compose.prod.yaml ${USER_AUTHORIZATION_SERVER}@${IP_AUTHORIZATION_SERVER}:${REMOTE_AUTH_DIR}/docker-compose.prod.yaml +echo "------------------------------------------------------" + +echo "------------------ START authorization server------------------" +ssh ${USER_AUTHORIZATION_SERVER}@${IP_AUTHORIZATION_SERVER} "cd ${REMOTE_AUTH_DIR} && docker compose -f docker-compose.yaml -f docker-compose.prod.yaml up -d --no-recreate --wait" +echo "------------------------------------------------------" + +echo "------------------ Create OAuth 2.0 client for only read------------------" + +code_client_readonly=$(ssh ${USER_AUTHORIZATION_SERVER}@${IP_AUTHORIZATION_SERVER} "cd authorization_server && docker compose -f docker-compose.yaml -f docker-compose.prod.yaml exec ory-hydra-oauth2-example-authorization-server-hydra \ + hydra create client \ + --endpoint http://127.0.0.1:4445 \ + --grant-type authorization_code,refresh_token \ + --response-type code,id_token \ + --format json \ + --scope openid --scope offline --scope read \ + --redirect-uri https://${DNS_CLIENT_READONLY}/api/login/oauth2/code/client-readonly") + +CLIENT_READONLY_CLIENT_ID=$(echo $code_client_readonly | jq -r '.client_id') +CLIENT_READONLY_CLIENT_SECRET=$(echo $code_client_readonly | jq -r '.client_secret') + +echo "------------------------------------------------------" +echo "------------------ Create OAuth 2.0 client for read and write ------------------" + +code_client_read_and_write=$(ssh ${USER_AUTHORIZATION_SERVER}@${IP_AUTHORIZATION_SERVER} "cd authorization_server && docker compose -f docker-compose.yaml -f docker-compose.prod.yaml exec ory-hydra-oauth2-example-authorization-server-hydra \ + hydra create client \ + --endpoint http://127.0.0.1:4445 \ + --grant-type authorization_code,refresh_token \ + --response-type code,id_token \ + --format json \ + --scope openid --scope offline --scope read --scope write \ + --redirect-uri https://${DNS_CLIENT_WRITE_AND_READ}/api/login/oauth2/code/client-write-and-read") + +CLIENT_WRITE_AND_READ_CLIENT_ID=$(echo $code_client_read_and_write | jq -r '.client_id') +CLIENT_WRITE_AND_READ_CLIENT_SECRET=$(echo $code_client_read_and_write | jq -r '.client_secret') +echo "------------------------------------------------------" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + +echo "------------------ RESOURCE SERVER ------------------" + +CA_RESOURCE_SERVER_NAME="ca_resource_server" +RESOURCE_DIR=ory_hydra_oauth2_example_resource_server +REMOTE_RESOURCE_DIR=resource_server + +echo "------------------ create folder for resource server------------------" +ssh ${USER_RESOURCE_SERVER}@${IP_RESOURCE_SERVER} "mkdir ${REMOTE_RESOURCE_DIR}" +echo "------------------------------------------------------" + +echo "------------------ remote copy ca-certificates for resource server ------------------" +cp ${CA_AUTH_SERVER_NAME}.crt ${RESOURCE_DIR}/ca-certificates/ +scp -r ${RESOURCE_DIR}/ca-certificates ${USER_RESOURCE_SERVER}@${IP_RESOURCE_SERVER}:${REMOTE_RESOURCE_DIR}/ca-certificates +rm ${RESOURCE_DIR}/ca-certificates/${CA_AUTH_SERVER_NAME}.crt +echo "------------------------------------------------------" + +echo "------------------ generate .env (docker compose) for resource server ------------------" + +echo IP_AUTHORIZATION_SERVER=${IP_AUTHORIZATION_SERVER} >> .env_resource_server +echo DNS_AUTHORIZATION_SERVER=${DNS_AUTHORIZATION_SERVER} >> .env_resource_server +echo HYDRA_INTROSPECT_USER=${HYDRA_INTROSPECT_USER} >>.env_resource_server +echo HYDRA_INTROSPECT_PASSWORD=${HYDRA_INTROSPECT_PASSWORD} >>.env_resource_server + +echo "------------------------------------------------------" +echo "------------------ generate CA gateway nginx for resource server ------------------" +generate_CA "${CA_RESOURCE_SERVER_NAME}" +echo "------------------------------------------------------" + +echo "------------------ generate certs gateway nginx for resource server ------------------" +generate_cert "${RESOURCE_DIR}" "${DNS_RESOURCE_SERVER}" "${CA_RESOURCE_SERVER_NAME}" +echo "------------------------------------------------------" + +echo "------------------ remote copy .env for resource server------------------" +scp .env_resource_server ${USER_RESOURCE_SERVER}@${IP_RESOURCE_SERVER}:${REMOTE_RESOURCE_DIR}/.env +rm .env_resource_server +echo "------------------------------------------------------" + +echo "------------------ remote copy nginx for resource server------------------" +DNS_RESOURCE_SERVER=${DNS_RESOURCE_SERVER} envsubst '$DNS_RESOURCE_SERVER' < ${RESOURCE_DIR}/nginx/confs/resource-server.conf.templ > ${RESOURCE_DIR}/nginx/confs/resource-server.conf +scp -r ${RESOURCE_DIR}/nginx ${USER_RESOURCE_SERVER}@${IP_RESOURCE_SERVER}:${REMOTE_RESOURCE_DIR}/nginx +rm ${RESOURCE_DIR}/nginx/confs/resource-server.conf +echo "------------------------------------------------------" + +echo "------------------ remote copy docker-compose.yaml for resource server------------------" +scp ${RESOURCE_DIR}/docker-compose.yaml ${USER_RESOURCE_SERVER}@${IP_RESOURCE_SERVER}:${REMOTE_RESOURCE_DIR}/docker-compose.yaml +echo "------------------------------------------------------" + +echo "------------------ START resource server------------------" +ssh ${USER_RESOURCE_SERVER}@${IP_RESOURCE_SERVER} "cd ${REMOTE_RESOURCE_DIR} && docker compose up -d --no-recreate --wait" +echo "------------------------------------------------------" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + +echo "------------------ CLIENT READONLY SERVER ------------------" + +CA_CLIENT_READONLY_SERVER_NAME="ca_client_readonly_server" +CLIENT_READONLY_DIR=ory_hydra_oauth2_example_client_readonly +REMOTE_CLIENT_READONLY_DIR=client_readonly + +echo "------------------ create folder for client readonly server------------------" +ssh ${USER_CLIENT_READONLY}@${IP_CLIENT_READONLY} "mkdir ${REMOTE_CLIENT_READONLY_DIR}" +echo "------------------------------------------------------" + +echo "------------------ remote copy ca-certificates for client readonly server ------------------" +cp ${CA_AUTH_SERVER_NAME}.crt ${CLIENT_READONLY_DIR}/ca-certificates/ +cp ${CA_RESOURCE_SERVER_NAME}.crt ${CLIENT_READONLY_DIR}/ca-certificates/ +scp -r ${CLIENT_READONLY_DIR}/ca-certificates ${USER_CLIENT_READONLY}@${IP_CLIENT_READONLY}:${REMOTE_CLIENT_READONLY_DIR}/ca-certificates +rm ${CLIENT_READONLY_DIR}/ca-certificates/${CA_AUTH_SERVER_NAME}.crt +rm ${CLIENT_READONLY_DIR}/ca-certificates/${CA_RESOURCE_SERVER_NAME}.crt +echo "------------------------------------------------------" + +echo "------------------ generate .env (docker compose) for client readonly server ------------------" + +echo IP_AUTHORIZATION_SERVER=${IP_AUTHORIZATION_SERVER} >> .env_readonly +echo IP_RESOURCE_SERVER=${IP_RESOURCE_SERVER} >> .env_readonly +echo DNS_AUTHORIZATION_SERVER=${DNS_AUTHORIZATION_SERVER} >> .env_readonly +echo DNS_RESOURCE_SERVER=${DNS_RESOURCE_SERVER} >> .env_readonly +echo CLIENT_READONLY_CLIENT_ID=${CLIENT_READONLY_CLIENT_ID} >>.env_readonly +echo CLIENT_READONLY_CLIENT_SECRET=${CLIENT_READONLY_CLIENT_SECRET} >>.env_readonly +echo DNS_CLIENT_READONLY=${DNS_CLIENT_READONLY} >>.env_readonly + +echo "------------------------------------------------------" +echo "------------------ generate CA gateway nginx for client readonly server ------------------" +generate_CA "${CA_CLIENT_READONLY_SERVER_NAME}" +echo "------------------------------------------------------" + +echo "------------------ generate certs gateway nginx for client readonly server ------------------" +generate_cert "${CLIENT_READONLY_DIR}" "${DNS_CLIENT_READONLY}" "${CA_CLIENT_READONLY_SERVER_NAME}" +echo "------------------------------------------------------" + +echo "------------------ remote copy .env for client readonly server------------------" +scp .env_readonly ${USER_CLIENT_READONLY}@${IP_CLIENT_READONLY}:${REMOTE_CLIENT_READONLY_DIR}/.env +rm .env_readonly +echo "------------------------------------------------------" + +echo "------------------ remote copy nginx for client readonly server------------------" +DNS_CLIENT_READONLY=${DNS_CLIENT_READONLY} envsubst '$DNS_CLIENT_READONLY' < ${CLIENT_READONLY_DIR}/nginx/confs/client-readonly.conf.templ > ${CLIENT_READONLY_DIR}/nginx/confs/client-readonly.conf +scp -r ${CLIENT_READONLY_DIR}/nginx ${USER_CLIENT_READONLY}@${IP_CLIENT_READONLY}:${REMOTE_CLIENT_READONLY_DIR}/nginx +rm ${CLIENT_READONLY_DIR}/nginx/confs/client-readonly.conf +echo "------------------------------------------------------" + +echo "------------------ remote copy docker-compose.yaml for client readonly server------------------" +scp ${CLIENT_READONLY_DIR}/docker-compose.yaml ${USER_CLIENT_READONLY}@${IP_CLIENT_READONLY}:${REMOTE_CLIENT_READONLY_DIR}/docker-compose.yaml +echo "------------------------------------------------------" + +echo "------------------ START client readonly server------------------" +ssh ${USER_CLIENT_READONLY}@${IP_CLIENT_READONLY} "cd ${REMOTE_CLIENT_READONLY_DIR} && docker compose up -d --no-recreate --wait" +echo "------------------------------------------------------" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + +echo "------------------ CLIENT WRITE AND READ SERVER ------------------" + +CA_CLIENT_WRITE_AND_READ_SERVER_NAME="ca_client_write_and_client_server" +CLIENT_WRITE_AND_READ_DIR=ory_hydra_oauth2_example_client_write_and_read +REMOTE_CLIENT_WRITE_AND_READ_DIR=client_write_and_read + +echo "------------------ create folder for client write and read server------------------" +ssh ${USER_CLIENT_WRITE_AND_READ}@${IP_CLIENT_WRITE_AND_READ} "mkdir ${REMOTE_CLIENT_WRITE_AND_READ_DIR}" +echo "------------------------------------------------------" + +echo "------------------ remote copy ca-certificates for client write and read server ------------------" +cp ${CA_AUTH_SERVER_NAME}.crt ${CLIENT_WRITE_AND_READ_DIR}/ca-certificates/ +cp ${CA_RESOURCE_SERVER_NAME}.crt ${CLIENT_WRITE_AND_READ_DIR}/ca-certificates/ +scp -r ${CLIENT_WRITE_AND_READ_DIR}/ca-certificates ${USER_CLIENT_WRITE_AND_READ}@${IP_CLIENT_WRITE_AND_READ}:${REMOTE_CLIENT_WRITE_AND_READ_DIR}/ca-certificates +rm ${CLIENT_WRITE_AND_READ_DIR}/ca-certificates/${CA_AUTH_SERVER_NAME}.crt +rm ${CLIENT_WRITE_AND_READ_DIR}/ca-certificates/${CA_RESOURCE_SERVER_NAME}.crt +echo "------------------------------------------------------" + +echo "------------------ generate .env (docker compose) for client write and read server ------------------" + +echo IP_AUTHORIZATION_SERVER=${IP_AUTHORIZATION_SERVER} >> .env_write_and_read +echo IP_RESOURCE_SERVER=${IP_RESOURCE_SERVER} >> .env_write_and_read +echo DNS_AUTHORIZATION_SERVER=${DNS_AUTHORIZATION_SERVER} >> .env_write_and_read +echo DNS_RESOURCE_SERVER=${DNS_RESOURCE_SERVER} >> .env_write_and_read +echo CLIENT_WRITE_AND_READ_CLIENT_ID=${CLIENT_WRITE_AND_READ_CLIENT_ID} >>.env_write_and_read +echo CLIENT_WRITE_AND_READ_CLIENT_SECRET=${CLIENT_WRITE_AND_READ_CLIENT_SECRET} >>.env_write_and_read +echo DNS_CLIENT_WRITE_AND_READ=${DNS_CLIENT_WRITE_AND_READ} >>.env_write_and_read + +echo "------------------------------------------------------" +echo "------------------ generate CA gateway nginx for client write and read server ------------------" +generate_CA "${CA_CLIENT_WRITE_AND_READ_SERVER_NAME}" +echo "------------------------------------------------------" + +echo "------------------ generate certs gateway nginx for client write and read server ------------------" +generate_cert "${CLIENT_WRITE_AND_READ_DIR}" "${DNS_CLIENT_WRITE_AND_READ}" "${CA_CLIENT_WRITE_AND_READ_SERVER_NAME}" +echo "------------------------------------------------------" + +echo "------------------ remote copy .env for client write and read server------------------" +scp .env_write_and_read ${USER_CLIENT_WRITE_AND_READ}@${IP_CLIENT_WRITE_AND_READ}:${REMOTE_CLIENT_WRITE_AND_READ_DIR}/.env +rm .env_write_and_read +echo "------------------------------------------------------" + +echo "------------------ remote copy nginx for client write and read server------------------" +DNS_CLIENT_WRITE_AND_READ=${DNS_CLIENT_WRITE_AND_READ} envsubst '$DNS_CLIENT_WRITE_AND_READ' < ${CLIENT_WRITE_AND_READ_DIR}/nginx/confs/client-write-and-read.conf.templ > ${CLIENT_WRITE_AND_READ_DIR}/nginx/confs/client-write-and-read.conf +scp -r ${CLIENT_WRITE_AND_READ_DIR}/nginx ${USER_CLIENT_WRITE_AND_READ}@${IP_CLIENT_WRITE_AND_READ}:${REMOTE_CLIENT_WRITE_AND_READ_DIR}/nginx +rm ${CLIENT_WRITE_AND_READ_DIR}/nginx/confs/client-write-and-read.conf +echo "------------------------------------------------------" + +echo "------------------ remote copy docker-compose.yaml for client write and read server------------------" +scp ${CLIENT_WRITE_AND_READ_DIR}/docker-compose.yaml ${USER_CLIENT_WRITE_AND_READ}@${IP_CLIENT_WRITE_AND_READ}:${REMOTE_CLIENT_WRITE_AND_READ_DIR}/docker-compose.yaml +echo "------------------------------------------------------" + +echo "------------------ START write and read server------------------" +ssh ${USER_CLIENT_WRITE_AND_READ}@${IP_CLIENT_WRITE_AND_READ} "cd ${REMOTE_CLIENT_WRITE_AND_READ_DIR} && docker compose up -d --no-recreate --wait" +echo "------------------------------------------------------" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/stop.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/stop.bash new file mode 100644 index 0000000..4c2b9db --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/stop.bash @@ -0,0 +1,21 @@ +#!/bin/bash + +source .env +echo "------------------ STOP ALL ------------------" + +echo "------------------ AUTHORIZATION SERVER ------------------" +REMOTE_AUTH_DIR=authorization_server +ssh ${USER_AUTHORIZATION_SERVER}@${IP_AUTHORIZATION_SERVER} "cd ${REMOTE_AUTH_DIR} && docker compose -f docker-compose.yaml -f docker-compose.prod.yaml down" + +echo "------------------ RESOURCE SERVER ------------------" +REMOTE_RESOURCE_DIR=resource_server +ssh ${USER_RESOURCE_SERVER}@${IP_RESOURCE_SERVER} "cd ${REMOTE_RESOURCE_DIR} && docker compose down" + +echo "------------------ CLIENT READONLY SERVER ------------------" +REMOTE_CLIENT_READONLY_DIR=client_readonly +ssh ${USER_CLIENT_READONLY}@${IP_CLIENT_READONLY} "cd ${REMOTE_CLIENT_READONLY_DIR} && docker compose down" + +echo "------------------ CLIENT WRITE AND READ SERVER ------------------" +REMOTE_CLIENT_WRITE_AND_READ_DIR=client_write_and_read +ssh ${USER_CLIENT_WRITE_AND_READ}@${IP_CLIENT_WRITE_AND_READ} "cd ${REMOTE_CLIENT_WRITE_AND_READ_DIR} && docker compose down" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" \ No newline at end of file diff --git a/ory-hydra-oauth2.0-spring-angular-docker-compose/stop_and_clear.bash b/ory-hydra-oauth2.0-spring-angular-docker-compose/stop_and_clear.bash new file mode 100644 index 0000000..d8cf0f6 --- /dev/null +++ b/ory-hydra-oauth2.0-spring-angular-docker-compose/stop_and_clear.bash @@ -0,0 +1,73 @@ +#!/bin/bash + +nginx_cert_rm(){ + rm -rf $1/nginx/cert +} + +source .env +echo "------------------ STOP ALL ------------------" + +echo "------------------ AUTHORIZATION SERVER ------------------" +AUTH_DIR=ory_hydra_oauth2_example_authorization_server +REMOTE_AUTH_DIR=authorization_server + +echo "------------------ STOP AND CLEAR authorization server------------------" +ssh ${USER_AUTHORIZATION_SERVER}@${IP_AUTHORIZATION_SERVER} "cd ${REMOTE_AUTH_DIR} && docker compose -f docker-compose.yaml -f docker-compose.prod.yaml down -v" +ssh ${USER_AUTHORIZATION_SERVER}@${IP_AUTHORIZATION_SERVER} "rm -rf ${REMOTE_AUTH_DIR}" +echo "------------------------------------------------------" + +echo "------------------ rm htpasswd_introspect authorization server ------------------" +rm htpasswd_introspect +echo "------------------------------------------------------" + +echo "------------------ rm nginx cert authorization server------------------" +nginx_cert_rm ${AUTH_DIR} +echo "------------------------------------------------------" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + + +echo "------------------ RESOURCE SERVER ------------------" +RESOURCE_DIR=ory_hydra_oauth2_example_resource_server +REMOTE_RESOURCE_DIR=resource_server + +echo "------------------ STOP AND CLEAR resource server------------------" +ssh ${USER_RESOURCE_SERVER}@${IP_RESOURCE_SERVER} "cd ${REMOTE_RESOURCE_DIR} && docker compose down -v" +ssh ${USER_RESOURCE_SERVER}@${IP_RESOURCE_SERVER} "rm -rf ${REMOTE_RESOURCE_DIR}" +echo "------------------------------------------------------" + +echo "------------------ rm nginx cert authorization server------------------" +nginx_cert_rm ${RESOURCE_DIR} +echo "------------------------------------------------------" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + +echo "------------------ CLIENT READONLY SERVER ------------------" +CLIENT_READONLY_DIR=ory_hydra_oauth2_example_client_readonly +REMOTE_CLIENT_READONLY_DIR=client_readonly + +echo "------------------ STOP AND CLEAR readonly server------------------" +ssh ${USER_CLIENT_READONLY}@${IP_CLIENT_READONLY} "cd ${REMOTE_CLIENT_READONLY_DIR} && docker compose down -v" +ssh ${USER_CLIENT_READONLY}@${IP_CLIENT_READONLY} "rm -rf ${REMOTE_CLIENT_READONLY_DIR}" +echo "------------------------------------------------------" + +echo "------------------ rm nginx cert readonly server------------------" +nginx_cert_rm ${CLIENT_READONLY_DIR} +echo "------------------------------------------------------" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + +echo "------------------ CLIENT WRITE AND READ SERVER ------------------" +CLIENT_WRITE_AND_READ_DIR=ory_hydra_oauth2_example_client_write_and_read +REMOTE_CLIENT_WRITE_AND_READ_DIR=client_write_and_read + +echo "------------------ STOP AND CLEAR write and read server------------------" +ssh ${USER_CLIENT_WRITE_AND_READ}@${IP_CLIENT_WRITE_AND_READ} "cd ${REMOTE_CLIENT_WRITE_AND_READ_DIR} && docker compose down -v" +ssh ${USER_CLIENT_WRITE_AND_READ}@${IP_CLIENT_WRITE_AND_READ} "rm -rf ${REMOTE_CLIENT_WRITE_AND_READ_DIR}" +echo "------------------------------------------------------" + +echo "------------------ rm nginx cert write and read server------------------" +nginx_cert_rm ${CLIENT_WRITE_AND_READ_DIR} +echo "------------------------------------------------------" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + +echo "------------------ rm ca_* ------------------" +rm ca_* +echo "------------------------------------------------------" \ No newline at end of file