# Iniciar sesión con URnetwork

Permite que la gente admita tu app o tu agente en su red privada, para que
pueda coordinarse con todo lo demás que tiene funcionando allí.

![El botón Iniciar sesión con URnetwork (Sign in with URnetwork) en sus dos variantes: el botón oscuro sobre una superficie clara y el botón claro sobre una superficie oscura](/docs-assets/sign-in-with-urnetwork-buttons.svg)

[Descarga el kit del botón](/docs-assets/sign-in-with-urnetwork-kit.zip): marcado,
estilos, las dos variantes como archivos individuales y el símbolo por
separado. El kit es definitivo y puedes adoptarlo hoy, antes que todo lo demás
de esta página.

> **En desarrollo: aún no está en producción.** Esta página describe un diseño
> en construcción. El servidor de autorización está escrito pero no desplegado:
> `auth.bringyour.com` aún no resuelve, y hoy no se puede llamar a ninguno de
> los endpoints de abajo. Los `client_id` se emiten a petición, no en
> autoservicio. Todo lo marcado como *planificado* está aún sin construir. El
> kit del botón es real y definitivo, y puedes adoptarlo ya.

## Qué es

Iniciar sesión es el paso de admisión. Lo que concede es un *lugar en una red*:
direccionable, descubrible por el resto del software de esa persona y
revocable por ella en cualquier momento.

Eso lo convierte en algo distinto de los botones de inicio de sesión a los que
se parece. No se trata sobre todo de saber quién es alguien. Se trata de que
te dejan entrar en algún sitio.

La concesión tiene dos mitades, que se consienten y se revocan por separado:

- **Identidad** — que una cuenta de URnetwork te aprobó, y nada más.
- **Red** — en qué red puedes actuar, nombrada en la pantalla de
  consentimiento y vinculada al token cuando se emite.

Una app puede pedir solo la identidad. Iniciar sesión nunca implica acceso a
la red.

## El botón

Usa el botón oscuro sobre superficies claras y de tono medio, y el botón claro
sobre las oscuras. No hay ningún script alojado: el kit es HTML, CSS y un SVG
en línea, así que colocar el botón en tu página no transmite nada hasta que
alguien hace clic en él.

Siempre que puedas, prefiere el marcado de abajo a los recursos SVG: así la
etiqueta se muestra con la propia tipografía de tu página y sigue siendo
seleccionable, buscable y traducible. Los SVG de recurso único del
[kit](/docs-assets/sign-in-with-urnetwork-kit.zip) son para los lugares que
solo admiten una imagen.

El botón dice **Sign in with** seguido del logotipo de URnetwork (lockup).
Solo la primera parte es texto, y eso es lo que permite traducir el botón
limpiamente: muestra "Sign in with" en el idioma de tu interfaz y no toques el
logotipo, para que la marca se lea igual en todas partes.

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

El logotipo se dibuja con `currentColor`, así que hereda el color del texto
del botón y una sola copia sirve para las dos variantes. El `aria-label` lleva
el nombre completo, ya que la mitad es un gráfico.

```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; }
```

La etiqueta usa una pila de fuentes del sistema en lugar de la tipografía de
marca de URnetwork, que no se puede redistribuir a otros sitios; además,
fijar una fuente web volvería a meter una petición de red en el botón.

### Reglas de marca

| | |
| --- | --- |
| Altura | 44px como mínimo; es también el área táctil. Nunca menos. |
| Radio de las esquinas | 12px |
| Logotipo | 16px de alto, `currentColor`; nunca redibujado, recoloreado ni reconstruido a partir de sus partes |
| Espacio libre | 8px por los cuatro lados |
| Anillo de foco | 2px `#0099ff`, con un desplazamiento de 2px |
| Fondo | `#101010` oscuro, `#f8f8f8` claro. Ningún otro color, ningún degradado. |

No compongas la palabra URnetwork con tu propia tipografía en lugar del
logotipo, no uses el símbolo sin la etiqueta, y no animes ni inclines el
botón. Traduce "Sign in with" al idioma de tu interfaz; el logotipo no cambia
nunca.

## Cómo funciona

*Planificado. Los endpoints de abajo no están desplegados.*

Código de autorización con PKCE, y nada más: esto es OAuth 2.1, así que no
hay concesión implícita y `S256` es obligatorio.

| | |
| --- | --- |
| Emisor | `https://auth.bringyour.com` |
| Descubrimiento | `/.well-known/openid-configuration` y `/.well-known/oauth-authorization-server` |
| Claves | `/.well-known/jwks.json` |
| Autorización | `https://ur.io/authorize` |
| Token | `POST /oauth/token` |
| UserInfo | `GET /oauth/userinfo` |
| Revocación | `POST /oauth/revoke` |

El endpoint de autorización está a propósito en un origen distinto del emisor.
La pantalla de consentimiento tiene que reutilizar la sesión iniciada, y el
almacenamiento del navegador está limitado a un origen, así que la página vive
donde ya está la sesión. El RFC 8414 lo permite y la identidad del emisor no
se ve afectada: sigue validando `iss` contra `auth.bringyour.com`.

El consentimiento se muestra siempre la primera vez que pides un conjunto dado
de ámbitos (scopes), aunque la persona ya haya iniciado sesión. Aprobar a un
tercero en silencio convertiría la página en un delegado confundido (confused
deputy).

## Unirse a la red

*Planificado.*

Un token de acceso de OAuth autoriza; no habla el protocolo `connect`. Para
ocupar el lugar en la red que se te concedió, intercámbialo:

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

Recibes un JWT de plataforma que lleva un `clientId`: la dirección por la que
te conoce el protocolo. A partir de ahí eres un miembro corriente de esa red,
y `/network/peers` es la forma de descubrir qué más ha sido admitido.

Tres cosas sorprenden a la gente:

1. Este es el único cruce entre los dos sistemas de credenciales, y es una
   decisión de autorización tomada en el servidor, no un token que se verifica
   en dos sitios. Los conjuntos de claves de firma siguen siendo disjuntos.
2. Aprovisionar un cliente **se factura a la red**. La pantalla de
   consentimiento lo dice; se repite aquí para que nadie se entere por una
   factura.
3. La credencial está limitada a la única red vinculada en la autorización.
   Nunca sigue a nadie a otra red.

Cuando dos apps necesitan saber que sirven a la misma persona, la referencia
compartida es la red en la que ambas fueron admitidas, no la persona. Es
deliberado: una red se consiente, se nombra en pantalla y se puede revocar,
mientras que un identificador de usuario duradero no sería nada de eso.

## Tokens

*Planificado.*

| Token | Duración | Notas |
| --- | --- | --- |
| De acceso | 1 hora | Ligado por audiencia a un solo recurso |
| De actualización | 90 días, con ventana deslizante | Opaco y rotatorio: cada uso retira el anterior |
| Token de ID | 1 hora | Dirigido a tu `client_id`; no lo envíes nunca a un servidor de recursos |

Los tokens de actualización rotan, y reutilizar uno ya retirado se trata como
un robo: se revoca toda la familia de tokens. Guarda el más reciente que se te
haya entregado.

## Requisitos para tu app

- **PKCE con `S256`.** No es opcional.
- **URI de redirección de coincidencia exacta**, registradas de antemano, solo
  HTTPS. Las apps nativas pueden usar `http://127.0.0.1:<port>/…`.
- **Verifica `iss`** en la respuesta de autorización: es lo que protege de los
  ataques de tipo mix-up.
- **Verifica `aud`** en el token de ID: debe ser igual a tu `client_id`.
- **No presentes un token de Iniciar sesión con URnetwork a la API del operador.**
  Los conjuntos de claves son disjuntos; no puede funcionar, y eso es el
  diseño, no una laguna.

## Obtener un `client_id`

La emisión se revisa; no es abierta. Una app admitida puede dirigirse a los
dispositivos de alguien, así que los clientes se examinan antes de que puedan
pedir identidad o acceso a la red. El registro dinámico sigue disponible para
los ámbitos exclusivos de MCP, cuyo alcance es una búsqueda de proveedores.

Aún no hay una consola de autoservicio. Pide un `client_id` y te emitiremos
uno.

## Revocación

*Planificado.*

La gente gestiona las concesiones desde la pantalla de apps conectadas
(connected apps) de la app de URnetwork, y revocar una elimina los clientes de
red que aprovisionó. Lo que observa tu app es que la sesión de connect se cae
y que el `clientId` deja de resolverse: una revocación, no un fallo.

Los tokens de acceso duran una hora, así que una revocación surte efecto
dentro de esa hora, no al instante.
