# Вход через URnetwork

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

![Кнопка входа через URnetwork (Sign in with URnetwork) в обоих вариантах: тёмная кнопка на светлой поверхности, светлая кнопка на тёмной поверхности](/docs-assets/sign-in-with-urnetwork-buttons.svg)

[Скачайте набор для кнопки](/docs-assets/sign-in-with-urnetwork-kit.zip) —
разметка, стили, оба варианта в виде отдельных ассетов и знак сам по себе.
Набор окончательный, и вы можете внедрить его уже сегодня, раньше всего
остального на этой странице.

> **В разработке — ещё не запущено.** Эта страница описывает проект, который
> ещё строится. Сервер авторизации написан, но не развёрнут:
> `auth.bringyour.com` пока не резолвится, и ни один из эндпоинтов ниже сегодня
> вызвать нельзя. `client_id` выдаются по запросу, а не в режиме
> самообслуживания. Всё, что помечено как *запланировано*, не построено. Набор
> для кнопки настоящий и окончательный, и вы можете внедрить его уже сейчас.

## Что это такое

Вход — это шаг допуска. Он даёт *место в сети* — адресуемое, обнаружимое
другими программами этого человека и отзываемое им в любой момент.

Этим он отличается от кнопок входа, на которые похож. Главное здесь не в том,
чтобы узнать, кто перед вами. Вас куда-то впускают.

Разрешение состоит из двух половин, на которые соглашаются и которые отзывают
по отдельности:

- **Идентичность** — то, что аккаунт URnetwork одобрил вас, и ничего больше.
- **Сеть** — в какой сети вы можете действовать; она названа на экране
  согласия и вшита в токен при его выдаче.

Приложение может запросить только идентичность. Вход никогда не подразумевает
доступа к сети.

## Кнопка

Используйте тёмную кнопку на светлых поверхностях и поверхностях среднего
тона, светлую — на тёмных. Размещённого скрипта нет: набор — это HTML, CSS и
один встроенный SVG, поэтому кнопка на вашей странице ничего не передаёт, пока
кто-нибудь на неё не нажмёт.

Где можете, предпочитайте разметку ниже SVG-ассетам: тогда подпись
отрисовывается шрифтом вашей страницы и остаётся выделяемой, доступной для
поиска и перевода. Отдельные SVG-ассеты из
[набора](/docs-assets/sign-in-with-urnetwork-kit.zip) — для мест, которые
принимают изображение и ничего больше.

На кнопке написано **Sign in with**, а следом идёт локап URnetwork. Текстом
является только первая часть, и именно поэтому кнопка чисто переводится:
выводите «Sign in with» на языке своего интерфейса и не трогайте локап, чтобы
бренд везде читался одинаково.

```html
<a class="ur-signin" href="YOUR_AUTHORIZE_URL" aria-label="Sign in with URnetwork">
  <span>Sign in with</span>
  <svg class="ur-signin-lockup" viewBox="0 0 980 139" fill="currentColor" aria-hidden="true">
    <!-- the lockup, 10 paths — copy them from the kit -->
  </svg>
</a>
```

Локап нарисован с `currentColor`, поэтому наследует цвет текста кнопки, и
одна копия служит обоим вариантам. `aria-label` несёт имя целиком, поскольку
половина его — графика.

```css
.ur-signin {
  display: inline-flex; align-items: center; gap: 10px;
  height: 44px; padding: 0 20px; min-width: 200px;
  border-radius: 12px; border: 1px solid transparent;
  background: #101010; color: #f8f8f8;
  font: 500 15px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  text-decoration: none; white-space: nowrap; cursor: pointer;
}
.ur-signin:hover         { background: #1c1c1c; }
.ur-signin:focus-visible { outline: 2px solid #0099ff; outline-offset: 2px; }
.ur-signin-lockup        { height: 16px; width: auto; flex: none; }

/* Light variant, for dark surfaces */
.ur-signin.is-light       { background: #f8f8f8; color: #101010; border-color: rgba(16,16,16,.14); }
.ur-signin.is-light:hover { background: #ececec; }
```

Подпись использует системный стек шрифтов, а не фирменный шрифт URnetwork,
который нельзя распространять на другие сайты, — а подключение веб-шрифта
вернуло бы в кнопку сетевой запрос.

### Правила бренда

| | |
| --- | --- |
| Высота | Минимум 44px — это же и зона касания. Никогда не меньше. |
| Радиус скругления | 12px |
| Локап | Высота 16px, `currentColor`; никогда не перерисовывается, не перекрашивается и не собирается заново из частей |
| Охранное поле | 8px со всех четырёх сторон |
| Кольцо фокуса | 2px `#0099ff`, отступ 2px |
| Фон | `#101010` — тёмный, `#f8f8f8` — светлый. Никакого другого цвета, никакого градиента. |

Не набирайте слово URnetwork своим шрифтом вместо локапа, не используйте знак
без подписи, не анимируйте и не наклоняйте кнопку. Переводите «Sign in with»
на язык своего интерфейса; локап не меняется никогда.

## Как это работает

*Запланировано. Эндпоинты ниже не развёрнуты.*

Код авторизации с PKCE — и ничего больше: это OAuth 2.1, поэтому неявного
гранта (implicit grant) нет, а `S256` обязателен.

| | |
| --- | --- |
| Издатель | `https://auth.bringyour.com` |
| Обнаружение | `/.well-known/openid-configuration` и `/.well-known/oauth-authorization-server` |
| Ключи | `/.well-known/jwks.json` |
| Авторизация | `https://ur.io/authorize` |
| Токен | `POST /oauth/token` |
| UserInfo | `GET /oauth/userinfo` |
| Отзыв | `POST /oauth/revoke` |

Эндпоинт авторизации намеренно находится на другом источнике (origin), чем
издатель. Экрану согласия нужно переиспользовать сеанс, в котором уже выполнен
вход, а хранилище браузера привязано к источнику, поэтому страница живёт там,
где сеанс уже есть. RFC 8414 это допускает, и идентичность издателя не
затрагивается — продолжайте проверять `iss` по `auth.bringyour.com`.

Согласие всегда показывается, когда вы впервые запрашиваете данный набор
областей доступа, даже если человек уже вошёл. Если бы страница молча
одобряла третью сторону, она стала бы запутавшимся заместителем (confused
deputy).

## Присоединение к сети

*Запланировано.*

Токен доступа OAuth авторизует; по протоколу `connect` он не говорит. Чтобы
занять место в сети, которое вам предоставили, обменяйте его:

```
POST /network/auth-client
Authorization: Bearer <access token>
```

Вы получаете JWT платформы с `clientId` — адресом, по которому вас знает
протокол. Дальше вы — обычный участник этой сети, а через `/network/peers` вы
узнаёте, что ещё в неё допущено.

Три вещи удивляют людей:

1. Это единственный переход между двумя системами учётных данных, и это
   решение об авторизации, принимаемое на сервере, а не один токен, который
   проходит проверку в двух местах. Наборы ключей подписи остаются
   непересекающимися.
2. За создание клиента **платит сеть**. Экран согласия об этом говорит; здесь
   это повторено, чтобы никто не узнал об этом из счёта.
3. Учётные данные ограничены одной сетью, привязанной при авторизации. За
   человеком в другую сеть они никогда не переходят.

Когда двум приложениям нужно знать, что они обслуживают одного и того же
человека, общим идентификатором служит сеть, в которую допустили их обоих, —
а не человек. Это намеренно: на сеть дано согласие, она названа на экране и
может быть отозвана, тогда как долговечный идентификатор пользователя не
обладал бы ни одним из этих свойств.

## Токены

*Запланировано.*

| Токен | Срок жизни | Примечания |
| --- | --- | --- |
| Токен доступа | 1 час | Привязан по аудитории к одному ресурсу |
| Токен обновления | 90 дней, скользящий | Непрозрачный и ротируемый — каждое использование выводит из оборота предыдущий |
| ID-токен | 1 час | Адресован вашему `client_id`; никогда не отправляйте его серверу ресурсов |

Токены обновления ротируются, а повторное использование выведенного из
оборота токена считается кражей: отзывается всё семейство токенов. Храните
самый новый из выданных вам.

## Требования к вашему приложению

- **PKCE с `S256`.** Не опционально.
- **URI перенаправления с точным совпадением**, зарегистрированные заранее,
  только HTTPS. Нативные приложения могут использовать
  `http://127.0.0.1:<port>/…`.
- **Проверяйте `iss`** в ответе авторизации — именно это защищает от атак
  типа mix-up.
- **Проверяйте `aud`** в ID-токене: он должен совпадать с вашим `client_id`.
- **Не предъявляйте API оператора токен входа через URnetwork.** Наборы
  ключей не пересекаются; это не может сработать, и так задумано, а не
  упущено.

## Получение `client_id`

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

Консоли самообслуживания пока нет. Запросите `client_id`, и мы его выдадим.

## Отзыв

*Запланировано.*

Люди управляют разрешениями на экране подключённых приложений (connected apps)
в приложении URnetwork, и отзыв разрешения удаляет созданных им сетевых
клиентов. Ваше приложение видит, как обрывается сеанс connect и `clientId`
перестаёт резолвиться, — это отзыв, а не сбой.

Токены доступа живут час, поэтому отзыв вступает в силу в течение часа, а не
мгновенно.
