# Iniciar sesión con UTILIA en Flask

Añade el botón «Iniciar sesión con UTILIA» a una aplicación Flask con Authlib, que descubre la configuración de UTILIA OS y verifica el id_token por ti.

Con Authlib, UTILIA OS funciona como cualquier proveedor OpenID Connect: lee su documento de descubrimiento, hace PKCE, canjea el código y verifica el `id_token`. Solo tienes que registrar el proveedor y escribir dos rutas.

## Antes de empezar

- Python 3.9 o superior.
- Activa OAuth en tu aplicación externa, con el tipo **Con servidor propio (recomendado)**, como explica [Activar OAuth en tu aplicación](https://os.utilia.ai/docs/developers/oauth/apps-panel).
- Añade como dirección de vuelta `http://localhost:5000/callback` para desarrollo y la de producción con tu dominio.

1. **Instala las dependencias**

   ```bash
   pip install flask authlib requests
   ```
2. **Registra el proveedor**

   ```python
   import os

   from authlib.integrations.flask_client import OAuth
   from flask import Flask

   app = Flask(__name__)
   app.secret_key = os.environ["FLASK_SECRET_KEY"]

   oauth = OAuth(app)
   oauth.register(
       name="utilia",
       client_id=os.environ["UTILIA_CLIENT_ID"],
       client_secret=os.environ["UTILIA_CLIENT_SECRET"],
       server_metadata_url="https://os.utilia.ai/.well-known/openid-configuration",
       client_kwargs={
           "scope": "openid profile email",
           # UTILIA OS exige PKCE con S256: sin esta línea, Authlib no lo envía.
           "code_challenge_method": "S256",
       },
   )
   ```

   > **Importante: No quites code_challenge_method**
   >
   > UTILIA OS rechaza cualquier autorización sin `code_challenge`, y Authlib solo genera PKCE si declaras `code_challenge_method` en `client_kwargs`.
3. **Crea la ruta de inicio de sesión**

   ```python
   from flask import url_for


   @app.route("/login")
   def login():
       redirect_uri = url_for("callback", _external=True)
       return oauth.utilia.authorize_redirect(redirect_uri)
   ```

   Lleva a la persona a la pantalla de autorización de UTILIA OS. Authlib genera y guarda en la sesión de Flask el `state`, el `nonce` y el `code_verifier`.
4. **Crea la ruta de vuelta**

   ```python
   from flask import redirect, session


   @app.route("/callback")
   def callback():
       token = oauth.utilia.authorize_access_token()
       userinfo = token["userinfo"]  # claims del id_token, ya verificado

       session["user"] = {
           "sub": userinfo["sub"],
           "name": userinfo.get("name"),
           "email": userinfo.get("email"),
           "picture": userinfo.get("picture"),
       }
       return redirect("/")
   ```

   `authorize_access_token()` comprueba el `state`, canjea el código y verifica la firma y los datos del `id_token`. Guarda `sub` como identificador de la persona: no cambia.

## Variables de entorno

```bash
export UTILIA_CLIENT_ID=utilia_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
export UTILIA_CLIENT_SECRET=tu_clave_secreta
export FLASK_SECRET_KEY=una_clave_aleatoria_larga
```

Nunca escribas la clave secreta en el código fuente.

## Ejemplo completo

```python
import os

from authlib.integrations.flask_client import OAuth
from flask import Flask, redirect, session, url_for
from markupsafe import escape

app = Flask(__name__)
app.secret_key = os.environ["FLASK_SECRET_KEY"]

oauth = OAuth(app)
oauth.register(
    name="utilia",
    client_id=os.environ["UTILIA_CLIENT_ID"],
    client_secret=os.environ["UTILIA_CLIENT_SECRET"],
    server_metadata_url="https://os.utilia.ai/.well-known/openid-configuration",
    client_kwargs={
        "scope": "openid profile email",
        "code_challenge_method": "S256",
    },
)


@app.route("/")
def index():
    user = session.get("user")
    if not user:
        return '<a href="/login">Iniciar sesión con UTILIA</a>'
    return f"<h1>Hola, {escape(user['name'])}</h1><a href='/logout'>Cerrar sesión</a>"


@app.route("/login")
def login():
    return oauth.utilia.authorize_redirect(url_for("callback", _external=True))


@app.route("/callback")
def callback():
    token = oauth.utilia.authorize_access_token()
    userinfo = token["userinfo"]
    session["user"] = {
        "sub": userinfo["sub"],
        "name": userinfo.get("name"),
        "email": userinfo.get("email"),
    }
    return redirect("/")


@app.route("/logout")
def logout():
    session.pop("user", None)
    return redirect("/")


if __name__ == "__main__":
    app.run(debug=True)
```

Si solo usas UTILIA OS para identificar a la persona, no necesitas guardar los tokens: basta con borrar `session["user"]` al cerrar sesión. Si los guardas para llamar a sus servidores MCP, hazlo en tu servidor (la cookie de sesión de Flask va firmada, pero no cifrada) y revócalos al cerrar sesión con `POST /api/oauth/revoke`, como explica [Cierre de sesión desde tu aplicación](https://os.utilia.ai/docs/developers/oauth/logout).

Para el estilo del botón, mira [Botones y marca](https://os.utilia.ai/docs/developers/oauth/buttons-and-branding). Si algo falla, [Problemas frecuentes con OAuth](https://os.utilia.ai/docs/developers/oauth/troubleshooting).
